A Trie (pronounced "try") is a tree-like data structure used for storing a dynamic set of strings, often for fast retrieval of words with common prefixes.
Operations in Trie
Insert (O(n))
Traverse the Trie following each character in the word.
If a character’s node does not exist, create a new node.
Mark the last node as an end of a word.
Search (O(n))
Traverse the Trie following each character in the word.
If we reach the last character and the node is marked as an end of a word, return true.
Otherwise, return false.
Starts With (Prefix Search) (O(n))
Similar to search, but instead of checking the end of word, just ensure the prefix exists.
Delete (O(n))
Perform a search for the word.
If found, unmark the last character’s end of word flag.
Recursively delete unused nodes (if they are not part of another word).
Trie Implementation in Java
class TrieNode {
TrieNode[] children;
boolean isEndOfWord;
public TrieNode() {
children = new TrieNode[26]; // Assuming only lowercase 'a' to 'z'
isEndOfWord = false;
}
}
public class Trie {
private final TrieNode root;
public Trie() {
root = new TrieNode();
}
// Insert a word into the trie
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; // Mark last node as end of word
}
// Search for a word in the trie
public boolean search(String word) {
TrieNode node = getNode(word);
return node != null && node.isEndOfWord;
}
// Check if a prefix exists in the trie
public boolean startsWith(String prefix) {
return getNode(prefix) != null;
}
// Helper method to get the last node of a given string
private TrieNode getNode(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
int index = ch - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
// Delete a word from the trie
public boolean delete(String word) {
return deleteHelper(root, word, 0);
}
private boolean deleteHelper(TrieNode node, String word, int depth) {
if (node == null) return false;
if (depth == word.length()) {
if (!node.isEndOfWord) return false;
node.isEndOfWord = false;
return isEmpty(node); // If true, delete node from parent
}
int index = word.charAt(depth) - 'a';
if (deleteHelper(node.children[index], word, depth + 1)) {
node.children[index] = null; // Remove reference if child is empty
return !node.isEndOfWord && isEmpty(node);
}
return false;
}
// Helper method to check if a node has children
private boolean isEmpty(TrieNode node) {
for (TrieNode child : node.children) {
if (child != null) return false;
}
return true;
}
// Print all words stored in Trie
public void printAllWords() {
printHelper(root, "");
}
private void printHelper(TrieNode node, String prefix) {
if (node.isEndOfWord) {
System.out.println(prefix);
}
for (char ch = 'a'; ch <= 'z'; ch++) {
int index = ch - 'a';
if (node.children[index] != null) {
printHelper(node.children[index], prefix + ch);
}
}
}
}
// Main class to test Trie
public class TrieDemo {
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("apple");
trie.insert("app");
trie.insert("bat");
System.out.println("Search 'apple': " + trie.search("apple")); // true
System.out.println("Search 'app': " + trie.search("app")); // true
System.out.println("Prefix 'ap': " + trie.startsWith("ap")); // true
System.out.println("Prefix 'bat': " + trie.startsWith("bat")); // true
System.out.println("Prefix 'b': " + trie.startsWith("b")); // true
System.out.println("Prefix 'cat': " + trie.startsWith("cat")); // false
System.out.println("Deleting 'apple': " + trie.delete("apple"));
System.out.println("Search 'apple': " + trie.search("apple")); // false
System.out.println("Search 'app': " + trie.search("app")); // true
System.out.println("All words in Trie:");
trie.printAllWords(); // Expected: app, bat
}
}