Code Templates
This page provides reusable trie implementations that you can adapt for various interview problems. Before diving in, make sure you understand the core concepts from the Concept Guide.
The Basic Trie Template
This template includes the core operations: insert, search, startsWith, and delete. It directly solves LeetCode 208: Implement Trie.
class TrieNode {
constructor() {
this.children = new Map();
this.isEndOfWord = false;
this.count = 0; // Words passing through this node
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
// Insert word into trie
insert(word) {
let node = this.root;
for (const char of word) {
if (!node.children.has(char)) {
node.children.set(char, new TrieNode());
}
node = node.children.get(char);
node.count++;
}
node.isEndOfWord = true;
}
// Check if word exists
search(word) {
const node = this._getNode(word);
return node !== null && node.isEndOfWord;
}
// Check if any word starts with prefix
startsWith(prefix) {
return this._getNode(prefix) !== null;
}
// Delete word from trie
delete(word) {
return this._deleteHelper(this.root, word, 0);
}
// Get all words with given prefix
getWordsWithPrefix(prefix) {
const node = this._getNode(prefix);
if (!node) return [];
const results = [];
this._collectWords(node, prefix, results);
return results;
}
// Helper: traverse to node for given word
_getNode(word) {
let node = this.root;
for (const char of word) {
if (!node.children.has(char)) {
return null;
}
node = node.children.get(char);
}
return node;
}
// Helper: recursive delete
_deleteHelper(node, word, index) {
if (index === word.length) {
if (!node.isEndOfWord) return false;
node.isEndOfWord = false;
return node.children.size === 0;
}
const char = word[index];
if (!node.children.has(char)) return false;
const shouldDelete = this._deleteHelper(
node.children.get(char), word, index + 1
);
if (shouldDelete) {
node.children.delete(char);
return node.children.size === 0 && !node.isEndOfWord;
}
return false;
}
// Helper: collect all words from node
_collectWords(node, current, results) {
if (node.isEndOfWord) {
results.push(current);
}
for (const [char, child] of node.children) {
this._collectWords(child, current + char, results);
}
}
}class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_word = False
self.count = 0 # Words passing through this node
class Trie:
def __init__(self):
self.root = TrieNode()
# Insert word into trie
def insert(self, word: str) -> None:
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.count += 1
node.is_end_of_word = True
# Check if word exists
def search(self, word: str) -> bool:
node = self._get_node(word)
return node is not None and node.is_end_of_word
# Check if any word starts with prefix
def starts_with(self, prefix: str) -> bool:
return self._get_node(prefix) is not None
# Delete word from trie
def delete(self, word: str) -> bool:
return self._delete_helper(self.root, word, 0)
# Get all words with given prefix
def get_words_with_prefix(self, prefix: str) -> list:
node = self._get_node(prefix)
if not node:
return []
results = []
self._collect_words(node, prefix, results)
return results
# Helper: traverse to node for given word
def _get_node(self, word: str):
node = self.root
for char in word:
if char not in node.children:
return None
node = node.children[char]
return node
# Helper: recursive delete
def _delete_helper(self, node, word: str, index: int) -> bool:
if index == len(word):
if not node.is_end_of_word:
return False
node.is_end_of_word = False
return len(node.children) == 0
char = word[index]
if char not in node.children:
return False
should_delete = self._delete_helper(
node.children[char], word, index + 1
)
if should_delete:
del node.children[char]
return len(node.children) == 0 and not node.is_end_of_word
return False
# Helper: collect all words from node
def _collect_words(self, node, current: str, results: list):
if node.is_end_of_word:
results.append(current)
for char, child in node.children.items():
self._collect_words(child, current + char, results)import java.util.*;
class TrieNode {
Map<Character, TrieNode> children;
boolean isEndOfWord;
int count; // Words passing through this node
public TrieNode() {
children = new HashMap<>();
isEndOfWord = false;
count = 0;
}
}
class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
// Insert word into trie
public void insert(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
node.children.putIfAbsent(ch, new TrieNode());
node = node.children.get(ch);
node.count++;
}
node.isEndOfWord = true;
}
// Check if word exists
public boolean search(String word) {
TrieNode node = getNode(word);
return node != null && node.isEndOfWord;
}
// Check if any word starts with prefix
public boolean startsWith(String prefix) {
return getNode(prefix) != null;
}
// Delete word from trie
public boolean delete(String word) {
return deleteHelper(root, word, 0);
}
// Get all words with given prefix
public List<String> getWordsWithPrefix(String prefix) {
TrieNode node = getNode(prefix);
if (node == null) return new ArrayList<>();
List<String> results = new ArrayList<>();
collectWords(node, prefix, results);
return results;
}
// Helper: traverse to node for given word
private TrieNode getNode(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (!node.children.containsKey(ch)) {
return null;
}
node = node.children.get(ch);
}
return node;
}
// Helper: recursive delete
private boolean deleteHelper(TrieNode node, String word, int index) {
if (index == word.length()) {
if (!node.isEndOfWord) return false;
node.isEndOfWord = false;
return node.children.size() == 0;
}
char ch = word.charAt(index);
if (!node.children.containsKey(ch)) return false;
boolean shouldDelete = deleteHelper(
node.children.get(ch), word, index + 1
);
if (shouldDelete) {
node.children.remove(ch);
return node.children.size() == 0 && !node.isEndOfWord;
}
return false;
}
// Helper: collect all words from node
private void collectWords(TrieNode node, String current, List<String> results) {
if (node.isEndOfWord) {
results.add(current);
}
for (Map.Entry<Character, TrieNode> entry : node.children.entrySet()) {
collectWords(entry.getValue(), current + entry.getKey(), results);
}
}
}#include <unordered_map>
#include <string>
#include <vector>
class TrieNode {
public:
std::unordered_map<char, TrieNode*> children;
bool isEndOfWord;
int count; // Words passing through this node
TrieNode() : isEndOfWord(false), count(0) {}
};
class Trie {
private:
TrieNode* root;
// Helper: traverse to node for given word
TrieNode* getNode(const std::string& word) {
TrieNode* node = root;
for (char ch : word) {
if (node->children.find(ch) == node->children.end()) {
return nullptr;
}
node = node->children[ch];
}
return node;
}
// Helper: recursive delete
bool deleteHelper(TrieNode* node, const std::string& word, int index) {
if (index == word.length()) {
if (!node->isEndOfWord) return false;
node->isEndOfWord = false;
return node->children.empty();
}
char ch = word[index];
if (node->children.find(ch) == node->children.end()) {
return false;
}
bool shouldDelete = deleteHelper(node->children[ch], word, index + 1);
if (shouldDelete) {
delete node->children[ch];
node->children.erase(ch);
return node->children.empty() && !node->isEndOfWord;
}
return false;
}
// Helper: collect all words from node
void collectWords(TrieNode* node, std::string current,
std::vector<std::string>& results) {
if (node->isEndOfWord) {
results.push_back(current);
}
for (const auto& pair : node->children) {
collectWords(pair.second, current + pair.first, results);
}
}
public:
Trie() {
root = new TrieNode();
}
// Insert word into trie
void insert(const std::string& word) {
TrieNode* node = root;
for (char ch : word) {
if (node->children.find(ch) == node->children.end()) {
node->children[ch] = new TrieNode();
}
node = node->children[ch];
node->count++;
}
node->isEndOfWord = true;
}
// Check if word exists
bool search(const std::string& word) {
TrieNode* node = getNode(word);
return node != nullptr && node->isEndOfWord;
}
// Check if any word starts with prefix
bool startsWith(const std::string& prefix) {
return getNode(prefix) != nullptr;
}
// Delete word from trie
bool deleteWord(const std::string& word) {
return deleteHelper(root, word, 0);
}
// Get all words with given prefix
std::vector<std::string> getWordsWithPrefix(const std::string& prefix) {
TrieNode* node = getNode(prefix);
if (!node) return {};
std::vector<std::string> results;
collectWords(node, prefix, results);
return results;
}
};package main
type TrieNode struct {
children map[rune]*TrieNode
isEndOfWord bool
count int // Words passing through this node
}
func NewTrieNode() *TrieNode {
return &TrieNode{
children: make(map[rune]*TrieNode),
isEndOfWord: false,
count: 0,
}
}
type Trie struct {
root *TrieNode
}
func NewTrie() *Trie {
return &Trie{root: NewTrieNode()}
}
// Insert word into trie
func (t *Trie) Insert(word string) {
node := t.root
for _, ch := range word {
if node.children[ch] == nil {
node.children[ch] = NewTrieNode()
}
node = node.children[ch]
node.count++
}
node.isEndOfWord = true
}
// Check if word exists
func (t *Trie) Search(word string) bool {
node := t.getNode(word)
return node != nil && node.isEndOfWord
}
// Check if any word starts with prefix
func (t *Trie) StartsWith(prefix string) bool {
return t.getNode(prefix) != nil
}
// Delete word from trie
func (t *Trie) Delete(word string) bool {
return t.deleteHelper(t.root, word, 0)
}
// Get all words with given prefix
func (t *Trie) GetWordsWithPrefix(prefix string) []string {
node := t.getNode(prefix)
if node == nil {
return []string{}
}
results := []string{}
t.collectWords(node, prefix, &results)
return results
}
// Helper: traverse to node for given word
func (t *Trie) getNode(word string) *TrieNode {
node := t.root
for _, ch := range word {
if node.children[ch] == nil {
return nil
}
node = node.children[ch]
}
return node
}
// Helper: recursive delete
func (t *Trie) deleteHelper(node *TrieNode, word string, index int) bool {
if index == len(word) {
if !node.isEndOfWord {
return false
}
node.isEndOfWord = false
return len(node.children) == 0
}
ch := rune(word[index])
if node.children[ch] == nil {
return false
}
shouldDelete := t.deleteHelper(node.children[ch], word, index+1)
if shouldDelete {
delete(node.children, ch)
return len(node.children) == 0 && !node.isEndOfWord
}
return false
}
// Helper: collect all words from node
func (t *Trie) collectWords(node *TrieNode, current string, results *[]string) {
if node.isEndOfWord {
*results = append(*results, current)
}
for ch, child := range node.children {
t.collectWords(child, current+string(ch), results)
}
}class TrieNode
attr_accessor :children, :is_end_of_word, :count
def initialize
@children = {}
@is_end_of_word = false
@count = 0 # Words passing through this node
end
end
class Trie
def initialize
@root = TrieNode.new
end
# Insert word into trie
def insert(word)
node = @root
word.each_char do |char|
node.children[char] ||= TrieNode.new
node = node.children[char]
node.count += 1
end
node.is_end_of_word = true
end
# Check if word exists
def search(word)
node = get_node(word)
!node.nil? && node.is_end_of_word
end
# Check if any word starts with prefix
def starts_with(prefix)
!get_node(prefix).nil?
end
# Delete word from trie
def delete(word)
delete_helper(@root, word, 0)
end
# Get all words with given prefix
def get_words_with_prefix(prefix)
node = get_node(prefix)
return [] if node.nil?
results = []
collect_words(node, prefix, results)
results
end
private
# Helper: traverse to node for given word
def get_node(word)
node = @root
word.each_char do |char|
return nil unless node.children[char]
node = node.children[char]
end
node
end
# Helper: recursive delete
def delete_helper(node, word, index)
if index == word.length
return false unless node.is_end_of_word
node.is_end_of_word = false
return node.children.empty?
end
char = word[index]
return false unless node.children[char]
should_delete = delete_helper(node.children[char], word, index + 1)
if should_delete
node.children.delete(char)
return node.children.empty? && !node.is_end_of_word
end
false
end
# Helper: collect all words from node
def collect_words(node, current, results)
results << current if node.is_end_of_word
node.children.each do |char, child|
collect_words(child, current + char, results)
end
end
endCode Breakdown
Key Variables
- root: The starting node representing an empty prefix
- children: Map/dictionary storing child nodes keyed by character
- isEndOfWord: Boolean flag marking complete words
- count: Optional counter for words passing through (useful for prefix frequency)
How the Template Works
flowchart TD
subgraph Insert["Insert Operation"]
I1[Start at root] --> I2{Character exists?}
I2 -->|No| I3[Create new node]
I2 -->|Yes| I4[Move to child]
I3 --> I4
I4 --> I5{More chars?}
I5 -->|Yes| I2
I5 -->|No| I6[Mark as word end]
end
subgraph Search["Search Operation"]
S1[Start at root] --> S2{Character exists?}
S2 -->|No| S3[Return false]
S2 -->|Yes| S4[Move to child]
S4 --> S5{More chars?}
S5 -->|Yes| S2
S5 -->|No| S6{Is word end?}
S6 -->|Yes| S7[Return true]
S6 -->|No| S3
end
Critical Sections
- Initialization: Create root node with empty children map
- Insert Loop: Traverse or create nodes for each character
- Search Loop: Traverse existing nodes, fail if any character missing
- End Marker: Always check
isEndOfWordto distinguish words from prefixes
Variations
Wildcard Search Template
Supports . as wildcard matching any character. Solves LeetCode 211: Design Add and Search Words Data Structure.
class WordDictionary {
constructor() {
this.root = new TrieNode();
}
addWord(word) {
let node = this.root;
for (const char of word) {
if (!node.children.has(char)) {
node.children.set(char, new TrieNode());
}
node = node.children.get(char);
}
node.isEndOfWord = true;
}
search(word) {
return this._dfs(word, 0, this.root);
}
_dfs(word, index, node) {
if (index === word.length) {
return node.isEndOfWord;
}
const char = word[index];
if (char === '.') {
// Try all possible children
for (const child of node.children.values()) {
if (this._dfs(word, index + 1, child)) {
return true;
}
}
return false;
} else {
if (!node.children.has(char)) {
return false;
}
return this._dfs(word, index + 1, node.children.get(char));
}
}
}class WordDictionary:
def __init__(self):
self.root = TrieNode()
def add_word(self, word: str) -> None:
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: str) -> bool:
return self._dfs(word, 0, self.root)
def _dfs(self, word: str, index: int, node) -> bool:
if index == len(word):
return node.is_end_of_word
char = word[index]
if char == '.':
# Try all possible children
for child in node.children.values():
if self._dfs(word, index + 1, child):
return True
return False
else:
if char not in node.children:
return False
return self._dfs(word, index + 1, node.children[char])class WordDictionary {
private TrieNode root;
public WordDictionary() {
root = new TrieNode();
}
public void addWord(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
node.children.putIfAbsent(ch, new TrieNode());
node = node.children.get(ch);
}
node.isEndOfWord = true;
}
public boolean search(String word) {
return dfs(word, 0, root);
}
private boolean dfs(String word, int index, TrieNode node) {
if (index == word.length()) {
return node.isEndOfWord;
}
char ch = word.charAt(index);
if (ch == '.') {
// Try all possible children
for (TrieNode child : node.children.values()) {
if (dfs(word, index + 1, child)) {
return true;
}
}
return false;
} else {
if (!node.children.containsKey(ch)) {
return false;
}
return dfs(word, index + 1, node.children.get(ch));
}
}
}class WordDictionary {
private:
TrieNode* root;
bool dfs(const std::string& word, int index, TrieNode* node) {
if (index == word.length()) {
return node->isEndOfWord;
}
char ch = word[index];
if (ch == '.') {
// Try all possible children
for (const auto& pair : node->children) {
if (dfs(word, index + 1, pair.second)) {
return true;
}
}
return false;
} else {
if (node->children.find(ch) == node->children.end()) {
return false;
}
return dfs(word, index + 1, node->children[ch]);
}
}
public:
WordDictionary() {
root = new TrieNode();
}
void addWord(const std::string& word) {
TrieNode* node = root;
for (char ch : word) {
if (node->children.find(ch) == node->children.end()) {
node->children[ch] = new TrieNode();
}
node = node->children[ch];
}
node->isEndOfWord = true;
}
bool search(const std::string& word) {
return dfs(word, 0, root);
}
};type WordDictionary struct {
root *TrieNode
}
func NewWordDictionary() *WordDictionary {
return &WordDictionary{root: NewTrieNode()}
}
func (wd *WordDictionary) AddWord(word string) {
node := wd.root
for _, ch := range word {
if node.children[ch] == nil {
node.children[ch] = NewTrieNode()
}
node = node.children[ch]
}
node.isEndOfWord = true
}
func (wd *WordDictionary) Search(word string) bool {
return wd.dfs(word, 0, wd.root)
}
func (wd *WordDictionary) dfs(word string, index int, node *TrieNode) bool {
if index == len(word) {
return node.isEndOfWord
}
ch := rune(word[index])
if ch == '.' {
// Try all possible children
for _, child := range node.children {
if wd.dfs(word, index+1, child) {
return true
}
}
return false
} else {
if node.children[ch] == nil {
return false
}
return wd.dfs(word, index+1, node.children[ch])
}
}class WordDictionary
def initialize
@root = TrieNode.new
end
def add_word(word)
node = @root
word.each_char do |char|
node.children[char] ||= TrieNode.new
node = node.children[char]
end
node.is_end_of_word = true
end
def search(word)
dfs(word, 0, @root)
end
private
def dfs(word, index, node)
return node.is_end_of_word if index == word.length
char = word[index]
if char == '.'
# Try all possible children
node.children.values.each do |child|
return true if dfs(word, index + 1, child)
end
false
else
return false unless node.children[char]
dfs(word, index + 1, node.children[char])
end
end
endArray-Based Trie for Fixed Alphabets
More memory efficient for small, fixed character sets:
// For lowercase letters only (26 characters)
class ArrayTrieNode {
constructor() {
this.children = new Array(26).fill(null);
this.isEndOfWord = false;
}
}
class ArrayTrie {
constructor() {
this.root = new ArrayTrieNode();
}
_charIndex(char) {
return char.charCodeAt(0) - 'a'.charCodeAt(0);
}
insert(word) {
let node = this.root;
for (const char of word) {
const idx = this._charIndex(char);
if (!node.children[idx]) {
node.children[idx] = new ArrayTrieNode();
}
node = node.children[idx];
}
node.isEndOfWord = true;
}
search(word) {
let node = this.root;
for (const char of word) {
const idx = this._charIndex(char);
if (!node.children[idx]) return false;
node = node.children[idx];
}
return node.isEndOfWord;
}
}# For lowercase letters only (26 characters)
class ArrayTrieNode:
def __init__(self):
self.children = [None] * 26
self.is_end_of_word = False
class ArrayTrie:
def __init__(self):
self.root = ArrayTrieNode()
def _char_index(self, char: str) -> int:
return ord(char) - ord('a')
def insert(self, word: str) -> None:
node = self.root
for char in word:
idx = self._char_index(char)
if not node.children[idx]:
node.children[idx] = ArrayTrieNode()
node = node.children[idx]
node.is_end_of_word = True
def search(self, word: str) -> bool:
node = self.root
for char in word:
idx = self._char_index(char)
if not node.children[idx]:
return False
node = node.children[idx]
return node.is_end_of_word// For lowercase letters only (26 characters)
class ArrayTrieNode {
ArrayTrieNode[] children;
boolean isEndOfWord;
public ArrayTrieNode() {
children = new ArrayTrieNode[26];
isEndOfWord = false;
}
}
class ArrayTrie {
private ArrayTrieNode root;
public ArrayTrie() {
root = new ArrayTrieNode();
}
private int charIndex(char ch) {
return ch - 'a';
}
public void insert(String word) {
ArrayTrieNode node = root;
for (char ch : word.toCharArray()) {
int idx = charIndex(ch);
if (node.children[idx] == null) {
node.children[idx] = new ArrayTrieNode();
}
node = node.children[idx];
}
node.isEndOfWord = true;
}
public boolean search(String word) {
ArrayTrieNode node = root;
for (char ch : word.toCharArray()) {
int idx = charIndex(ch);
if (node.children[idx] == null) return false;
node = node.children[idx];
}
return node.isEndOfWord;
}
}// For lowercase letters only (26 characters)
class ArrayTrieNode {
public:
ArrayTrieNode* children[26];
bool isEndOfWord;
ArrayTrieNode() : isEndOfWord(false) {
for (int i = 0; i < 26; i++) {
children[i] = nullptr;
}
}
};
class ArrayTrie {
private:
ArrayTrieNode* root;
int charIndex(char ch) {
return ch - 'a';
}
public:
ArrayTrie() {
root = new ArrayTrieNode();
}
void insert(const std::string& word) {
ArrayTrieNode* node = root;
for (char ch : word) {
int idx = charIndex(ch);
if (!node->children[idx]) {
node->children[idx] = new ArrayTrieNode();
}
node = node->children[idx];
}
node->isEndOfWord = true;
}
bool search(const std::string& word) {
ArrayTrieNode* node = root;
for (char ch : word) {
int idx = charIndex(ch);
if (!node->children[idx]) return false;
node = node->children[idx];
}
return node->isEndOfWord;
}
};// For lowercase letters only (26 characters)
type ArrayTrieNode struct {
children [26]*ArrayTrieNode
isEndOfWord bool
}
type ArrayTrie struct {
root *ArrayTrieNode
}
func NewArrayTrie() *ArrayTrie {
return &ArrayTrie{root: &ArrayTrieNode{}}
}
func charIndex(ch rune) int {
return int(ch - 'a')
}
func (t *ArrayTrie) Insert(word string) {
node := t.root
for _, ch := range word {
idx := charIndex(ch)
if node.children[idx] == nil {
node.children[idx] = &ArrayTrieNode{}
}
node = node.children[idx]
}
node.isEndOfWord = true
}
func (t *ArrayTrie) Search(word string) bool {
node := t.root
for _, ch := range word {
idx := charIndex(ch)
if node.children[idx] == nil {
return false
}
node = node.children[idx]
}
return node.isEndOfWord
}# For lowercase letters only (26 characters)
class ArrayTrieNode
attr_accessor :children, :is_end_of_word
def initialize
@children = Array.new(26)
@is_end_of_word = false
end
end
class ArrayTrie
def initialize
@root = ArrayTrieNode.new
end
def insert(word)
node = @root
word.each_char do |char|
idx = char_index(char)
node.children[idx] ||= ArrayTrieNode.new
node = node.children[idx]
end
node.is_end_of_word = true
end
def search(word)
node = @root
word.each_char do |char|
idx = char_index(char)
return false unless node.children[idx]
node = node.children[idx]
end
node.is_end_of_word
end
private
def char_index(char)
char.ord - 'a'.ord
end
endPractice
Ready to apply these templates? Head to the Practice Problems page for curated LeetCode problems that use the trie pattern.
Pro Tip: When adapting these templates, focus on the core loop structure. Most trie problems follow the same traversal pattern - the difference is what you do at each node or at the end of traversal.