0
0
DSA Goprogramming

Word Search in Trie in DSA Go

Choose your learning style9 modes available
Mental Model
A trie stores words by sharing common prefixes, making it easy to check if a word exists by following letters step-by-step.
Analogy: Imagine a tree of roads where each road sign is a letter. To find a word, you follow the signs letter by letter until you reach the end of the word.
root
 ↓
 a -> b -> c (end)
  ↓
  d (end)

Each arrow -> shows the next letter link.
'end' marks a complete word.
Dry Run Walkthrough
Input: Insert words: "cat", "car", "dog"; Search for "car"
Goal: Check if the word "car" exists in the trie
Step 1: Insert 'cat' letter by letter
root -> c -> a -> t [end]
root -> null
Why: Build path for 'cat' so we can find it later
Step 2: Insert 'car' letter by letter, sharing prefix 'ca'
root -> c -> a -> t [end]
               ↓
               r [end]
Why: Reuse 'ca' nodes, add 'r' to mark 'car'
Step 3: Insert 'dog' letter by letter
root -> c -> a -> t [end]
               ↓
               r [end]
root -> d -> o -> g [end]
Why: Add new branch for 'dog'
Step 4: Search for 'car' by following letters c -> a -> r
root -> c -> a -> r [end]
Why: All letters found and 'r' marks end of word, so 'car' exists
Result:
root -> c -> a -> t [end]
               ↓
               r [end]
root -> d -> o -> g [end]
Search result: true
Annotated Code
DSA Go
package main

import "fmt"

// TrieNode represents each node in the trie
type TrieNode struct {
	children map[rune]*TrieNode
	isEnd bool
}

// Trie structure with root node
type Trie struct {
	root *TrieNode
}

// NewTrieNode creates a new trie node
func NewTrieNode() *TrieNode {
	return &TrieNode{children: make(map[rune]*TrieNode)}
}

// NewTrie creates a new trie
func NewTrie() *Trie {
	return &Trie{root: NewTrieNode()}
}

// Insert adds a word to the trie
func (t *Trie) Insert(word string) {
	curr := t.root
	for _, ch := range word {
		if _, ok := curr.children[ch]; !ok {
			curr.children[ch] = NewTrieNode() // create node if missing
		}
		curr = curr.children[ch] // move to next node
	}
	curr.isEnd = true // mark end of word
}

// Search checks if a word exists in the trie
func (t *Trie) Search(word string) bool {
	curr := t.root
	for _, ch := range word {
		if _, ok := curr.children[ch]; !ok {
			return false // letter missing, word not found
		}
		curr = curr.children[ch] // move to next node
	}
	return curr.isEnd // true if end of word
}

func main() {
	trie := NewTrie()
	trie.Insert("cat")
	trie.Insert("car")
	trie.Insert("dog")

	fmt.Println(trie.Search("car"))
}
if _, ok := curr.children[ch]; !ok {
check if current letter node exists
curr.children[ch] = NewTrieNode() // create node if missing
create new node for letter if missing
curr = curr.children[ch] // move to next node
advance current pointer to next letter node
curr.isEnd = true // mark end of a complete word
mark node as end of a complete word
if _, ok := curr.children[ch]; !ok { return false }
if letter missing during search, word not found
return curr.isEnd // true if end of word
return true only if last letter marks word end
OutputSuccess
true
Complexity Analysis
Time: O(m) because we process each of the m letters in the word once during insert or search
Space: O(m) for insert because new nodes may be created for each letter; search uses O(1) extra space
vs Alternative: Compared to searching in a list of words (O(n*m)), trie search is faster as it avoids scanning all words
Edge Cases
search for empty string
returns false because empty word is not inserted
DSA Go
return curr.isEnd // true if end of word
search for word not inserted
returns false as missing letter node detected
DSA Go
if _, ok := curr.children[ch]; !ok { return false }
insert duplicate word
no new nodes created, end marker remains true
DSA Go
curr.isEnd = true // mark end of word
When to Use This Pattern
When you need to quickly check if words or prefixes exist in a large set, use a trie because it shares common prefixes and speeds up search.
Common Mistakes
Mistake: Not marking the end of a word, causing search to return true for prefixes instead of full words
Fix: Always set isEnd = true at the last node of inserted words
Mistake: Not checking if a child node exists before moving, causing runtime errors
Fix: Check if child node exists before moving; if missing, create node (insert) or return false (search)
Summary
Stores words letter by letter sharing prefixes to enable fast word search.
Use when you want to check if words exist efficiently among many words.
The key is to follow letters step-by-step and mark where words end.