Skip to main content

Trie

Author: @wingkwong

Overview

208. Implement Trie (Prefix Tree) will be used as an example.

A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.

Implement the Trie class:

  • Trie() Initializes the trie object.
  • void insert(String word) Inserts the string word into the trie.
  • boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.
  • boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

Example 1:

Input
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output
[null, null, true, false, true, null, true]

Explanation
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // return True
trie.search("app"); // return False
trie.startsWith("app"); // return True
trie.insert("app");
trie.search("app"); // return True

Constraints:

  • 1<=word.length,prefix.length<=20001 <= word.length, prefix.length <= 2000
  • word and prefix consist only of lowercase English letters.
  • At most 3 * 10^4 calls in total will be made to insert, search, and startsWith.

Trie

We can see Trie containing a number of Trie nodes. Each node contains a value and links to other nodes. We start from the root, we traverse till ee so that we have geegee. At this node, we have three different nodes to traverse so that we have geekgeek, geergeer, and geezgeez. We can also further to have geeksgeeks and geektgeekt and so on.

Diagram Source: https://www.geeksforgeeks.org/

Trie Node

Each Trie Node should have a children array with the size of 2626 for character aa to zz. Also it has a boolean variable isEndOfWordisEndOfWord to indicate if a word is ended at this node.

class TrieNode {
public:
// is a word ended at this node
bool isEndOfWord;
// children for 26 characters
TrieNode* children[26];

// constructor - setting initial values
TrieNode() {
// no word is ended here
isEndOfWord = false;
// not linking to other Trie node
for (int i = 0; i < 26; i++) {
children[i] = NULL;
}
}
};

Initializing

Trie() { 
// init Trie - define the very first node
root = new TrieNode();
}

Searching

Given a word, search returns if the word is in the trie.

bool search(string word) {
// start from the root node
TrieNode* node = root;
// iterate the word
for (int i = 0; i < (int) word.size(); i++) {
// get the index of the character
// a -> 0
// b -> 1
// ...
// z -> 25
int idx = word[i] - 'a';
// if there is no such node, that means the word doesn't exist
if (!node->children[idx]) return false;
// otherwise, traverse the next node
node = node->children[idx];
}
// check if this node is marked with isEndOfWord = true
return node->isEndOfWord;
}

Insertion

Given a word, insert inserts it into the trie.

void insert(string word) {
// start from the root node
TrieNode* node = root;
for (int i = 0; i < (int) word.size(); i++) {
// get the index of the character
// a -> 0
// b -> 1
// ...
// z -> 25
int idx = word[i] - 'a';
// traverse each node,
if (!node->children[idx]) {
// if the node doesn't exist,
// create a new node
node->children[idx] = new TrieNode();
}
// traverse the next one
node = node->children[idx];
}
// mark this node with isEndOfWord = true
node->isEndOfWord = true;
}

startsWith

Given a prefix, startsWith checks if there is any word in the trie that starts with the given prefix.

bool startsWith(string prefix) {
// start from the root node
TrieNode* node = root;
// iterate each character in prefix
for (int i = 0; i < (int) prefix.size(); i++) {
// get the index of the character
// a -> 0
// b -> 1
// ...
// z -> 25
int idx = prefix[i] - 'a';
// if there is no such node, that means the word doesn't exist
if (!node->children[idx]) return false;
// otherwise, traverse the next node
node = node->children[idx];
}
// all target nodes have been traversed, return true here
return true;
}