Search Word in Trie
Problem and examples
true if the word is found and fully matches a word previously inserted, otherwise return false.
| Inserted Words | Search Word | Expected Result | Explanation |
|---|---|---|---|
| ["apple", "banana", "grape"] | "banana" | true | "banana" was inserted and fully matches, so the search returns true. Visualization |
| ["apple", "banana", "grape"] | "ban" | false | "ban" is a prefix of "banana" but not a complete word in the Trie. Visualization |
| [] | "apple" | false | No words are inserted, so the search will always return false. Visualization |
| ["cat", "dog"] | "" | false | An empty search string is not considered a valid word in the Trie.Visualization |
| ["one", "only", "once"] | "only" | true | The word "only" exactly matches a word in the Trie. Visualization |
| ["one", "only", "once"] | "on" | false | "on" is a prefix shared by multiple words but is not a full word in the Trie. Visualization |
Explore Search Word in Trie
Load an example or enter your own input. Use the player controls to follow each change.
To determine whether a word exists in a Trie, we perform a character-by-character traversal starting from the root node. For each character in the search word, we check if the character exists in the current node's children. If it does, we move to that child node and continue. If we encounter a character that doesn't exist in the Trie at any point, we know immediately that the word does not exist, so we return false.
However, reaching the end of the word isn't enough — we must also ensure that the last character leads us to a node that is explicitly marked as the end of a valid word. If not, then the word is only a prefix of some longer word and should still return false.
Let’s consider some cases:
Normal case:
If we insert ["one", "only", "once"] and search for "once", we follow each letter until the end and confirm it ends at a valid word node. So, returntrue. VisualizationPrefix case:
Searching for "on" in the same Trie reaches valid characters but doesn’t hit an end-of-word node, so we returnfalse. Visualization- Empty Trie: If no words are inserted, any search word will return
falsebecause the Trie is completely empty. - Empty string search: Searching with an empty string is often considered invalid unless explicitly allowed. In most Trie implementations, this will return
false.
This operation is highly efficient — its time complexity is O(m), where m is the length of the search word, making it much faster than linear search for large sets of words.
- Start from the root node of the Trie.
- For each character in the input word:
- Check if the character exists in the current node's children map.
- If it does, move to the corresponding child node.
- If it does not, return
false— the word is not present in the Trie.
- After traversing all characters, check if the current node is marked as the end of a valid word.
- Return
trueonly if the final node signifies the end of a word; otherwise, returnfalse.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ALPHABET_SIZE 26
typedef struct TrieNode {
struct TrieNode *children[ALPHABET_SIZE];
bool isEndOfWord;
} TrieNode;
TrieNode* createNode() {
TrieNode* node = (TrieNode*)malloc(sizeof(TrieNode));
node->isEndOfWord = false;
for (int i = 0; i < ALPHABET_SIZE; i++) {
node->children[i] = NULL;
}
return node;
}
void insert(TrieNode* root, const char* word) {
TrieNode* node = root;
while (*word) {
int index = *word - 'a';
if (!node->children[index]) {
node->children[index] = createNode();
}
node = node->children[index];
word++;
}
node->isEndOfWord = true;
}
bool search(TrieNode* root, const char* word) {
TrieNode* node = root;
while (*word) {
int index = *word - 'a';
if (!node->children[index]) {
return false;
}
node = node->children[index];
word++;
}
return node->isEndOfWord;
}
int main() {
TrieNode* root = createNode();
insert(root, "apple");
insert(root, "app");
printf("%s\n", search(root, "apple") ? "true" : "false"); // true
printf("%s\n", search(root, "appl") ? "true" : "false"); // false
return 0;
}C++ implementation
#include <iostream>
using namespace std;
class TrieNode {
public:
TrieNode* children[26];
bool isEndOfWord;
TrieNode() {
isEndOfWord = false;
for (int i = 0; i < 26; ++i)
children[i] = nullptr;
}
};
class Trie {
private:
TrieNode* root;
public:
Trie() {
root = new TrieNode();
}
void insert(string word) {
TrieNode* node = root;
for (char ch : word) {
int index = ch - 'a';
if (!node->children[index])
node->children[index] = new TrieNode();
node = node->children[index];
}
node->isEndOfWord = true;
}
bool search(string word) {
TrieNode* node = root;
for (char ch : word) {
int index = ch - 'a';
if (!node->children[index]) return false;
node = node->children[index];
}
return node->isEndOfWord;
}
};
int main() {
Trie trie;
trie.insert("apple");
trie.insert("app");
cout << boolalpha << trie.search("apple") << endl; // true
cout << trie.search("appl") << endl; // false
return 0;
}Python implementation
class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_word = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end_of_word = True
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end_of_word
if __name__ == "__main__":
trie = Trie()
trie.insert("apple")
trie.insert("app")
print(trie.search("apple")) # True
print(trie.search("appl")) # FalseJava implementation
class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isEndOfWord = false;
}
public class Trie {
private final TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new TrieNode();
}
node = node.children[index];
}
node.isEndOfWord = true;
}
public boolean search(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
int index = ch - 'a';
if (node.children[index] == null) {
return false;
}
node = node.children[index];
}
return node.isEndOfWord;
}
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("apple");
trie.insert("app");
System.out.println(trie.search("apple")); // true
System.out.println(trie.search("appl")); // false
}
}JavaScript implementation
class TrieNode {
constructor() {
this.children = {};
this.isEndOfWord = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let node = this.root;
for (let char of word) {
if (!node.children[char]) {
node.children[char] = new TrieNode();
}
node = node.children[char];
}
node.isEndOfWord = true;
}
search(word) {
let node = this.root;
for (let char of word) {
if (!node.children[char]) {
return false;
}
node = node.children[char];
}
return node.isEndOfWord;
}
}
// Example usage:
const trie = new Trie();
trie.insert("apple");
trie.insert("app");
console.log(trie.search("apple")); // true
console.log(trie.search("appl")); // false;Go implementation
package main
import (
"fmt"
)
type TrieNode struct {
children map[rune]*TrieNode
isEndOfWord bool
}
func NewTrieNode() *TrieNode {
return &TrieNode{children: make(map[rune]*TrieNode)}
}
type Trie struct {
root *TrieNode
}
func NewTrie() *Trie {
return &Trie{root: NewTrieNode()}
}
func (t *Trie) Insert(word string) {
node := t.root
for _, ch := range word {
if _, exists := node.children[ch]; !exists {
node.children[ch] = NewTrieNode()
}
node = node.children[ch]
}
node.isEndOfWord = true
}
func (t *Trie) Search(word string) bool {
node := t.root
for _, ch := range word {
if _, exists := node.children[ch]; !exists {
return false
}
node = node.children[ch]
}
return node.isEndOfWord
}
func main() {
trie := NewTrie()
trie.Insert("apple")
trie.Insert("app")
fmt.Println(trie.Search("apple")) // true
fmt.Println(trie.Search("appl")) // false
}Rust implementation
use std::collections::HashMap;
struct TrieNode {
children: HashMap<char, TrieNode>,
is_end_of_word: bool,
}
impl TrieNode {
fn new() -> Self {
TrieNode {
children: HashMap::new(),
is_end_of_word: false,
}
}
}
struct Trie {
root: TrieNode,
}
impl Trie {
fn new() -> Self {
Trie {
root: TrieNode::new(),
}
}
fn insert(&mut self, word: &str) {
let mut node = &mut self.root;
for ch in word.chars() {
node = node.children.entry(ch).or_insert_with(TrieNode::new);
}
node.is_end_of_word = true;
}
fn search(&self, word: &str) -> bool {
let mut node = &self.root;
for ch in word.chars() {
match node.children.get(&ch) {
Some(next_node) => node = next_node,
None => return false,
}
}
node.is_end_of_word
}
}
fn main() {
let mut trie = Trie::new();
trie.insert("apple");
trie.insert("app");
println!("{}", trie.search("apple")); // true
println!("{}", trie.search("appl")); // false
}Kotlin implementation
class TrieNode {
val children = Array<TrieNode?>(26) { null }
var isEndOfWord = false
}
class Trie {
private val root = TrieNode()
fun insert(word: String) {
var node = root
for (ch in word) {
val index = ch - 'a'
if (node.children[index] == null) {
node.children[index] = TrieNode()
}
node = node.children[index]!!
}
node.isEndOfWord = true
}
fun search(word: String): Boolean {
var node = root
for (ch in word) {
val index = ch - 'a'
node = node.children[index] ?: return false
}
return node.isEndOfWord
}
}
fun main() {
val trie = Trie()
trie.insert("apple")
trie.insert("app")
println(trie.search("apple")) // true
println(trie.search("appl")) // false
}Swift implementation
class TrieNode {
var children: [Character: TrieNode] = [:]
var isEndOfWord = false
}
class Trie {
private let root = TrieNode()
func insert(_ word: String) {
var node = root
for ch in word {
if node.children[ch] == nil {
node.children[ch] = TrieNode()
}
node = node.children[ch]!
}
node.isEndOfWord = true
}
func search(_ word: String) -> Bool {
var node = root
for ch in word {
guard let nextNode = node.children[ch] else {
return false
}
node = nextNode
}
return node.isEndOfWord
}
}
let trie = Trie()
trie.insert("apple")
trie.insert("app")
print(trie.search("apple")) // true
print(trie.search("appl")) // falseTypeScript implementation
class TrieNode {
children: { [key: string]: TrieNode } = {};
isEndOfWord: boolean = false;
}
class Trie {
private root: TrieNode = new TrieNode();
insert(word: string): void {
let node = this.root;
for (const ch of word) {
if (!node.children[ch]) {
node.children[ch] = new TrieNode();
}
node = node.children[ch];
}
node.isEndOfWord = true;
}
search(word: string): boolean {
let node = this.root;
for (const ch of word) {
if (!node.children[ch]) {
return false;
}
node = node.children[ch];
}
return node.isEndOfWord;
}
}
const trie = new Trie();
trie.insert("apple");
trie.insert("app");
console.log(trie.search("apple")); // true
console.log(trie.search("appl")); // false;Time complexity
| Case | Time | Explanation |
|---|---|---|
| best case | O(k) | The algorithm must traverse each character of the input word (of length k) even if all characters are found immediately in the Trie. Therefore, best case is linear in the length of the word. |
| average case | O(k) | On average, the algorithm traverses all k characters of the word to determine if it exists in the Trie. Each character check takes constant time using a hash map or array lookup. |
| worst case | O(k) | In the worst case, the word is either not present or only partially present, but the algorithm still examines all k characters before failing. Each character requires a constant-time check in the children map. |
Space complexity
O(1)
The search operation uses only a constant amount of extra space for pointers or references during traversal. No additional data structures are created proportional to input size.