Java - Implementing a Trie in Java
When working with strings in programming, especially in problems like autocomplete, spell checking, and word searching, an efficient data structure called a Trie becomes extremely useful.
In this blog, we'll explore what a Trie is, why you might want to use it, and how you can implement it in Java.
What is a Trie?
A Trie (pronounced try) is a tree-like data structure that stores a dynamic set of strings. It is often used for retrieval of a key in a dataset of strings. Keys are usually words, and nodes represent characters of the word.
Key points:
Each node stores a character.
A path from the root to a node represents a prefix.
Words sharing a prefix share part of the path.
For example, the words cat, cap, and can will share the first two characters ca in the Trie.
Why Use a Trie?
Fast search: Searching for a word of length L takes O(L) time.
Efficient storage: Common prefixes are stored only once.
Prefix matching: Easy to find all words starting with a given prefix.
Basic Trie Operations
The main operations we typically want in a Trie are:
Insert a word
Search for a word
Check if any word starts with a given prefix
Let's see how we can implement these in Java.
Implementing a Trie in Java
Here's a simple and clean Trie implementation:
import java.util.HashMap;
class TrieNode {
HashMap<Character, TrieNode> children;
boolean isEndOfWord;
public TrieNode() {
children = new HashMap<>();
isEndOfWord = false;
}
}
class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
// Insert a word into the Trie
public void insert(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
node.children.putIfAbsent(c, new TrieNode());
node = node.children.get(c);
}
node.isEndOfWord = true;
}
// Search for a word in the Trie
public boolean search(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
node = node.children.get(c);
if (node == null) {
return false;
}
}
return node.isEndOfWord;
}
// Check if any word starts with the given prefix
public boolean startsWith(String prefix) {
TrieNode node = root;
for (char c : prefix.toCharArray()) {
node = node.children.get(c);
if (node == null) {
return false;
}
}
return true;
}
// Example usage
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("cat");
trie.insert("cap");
trie.insert("can");
System.out.println("Search cat: " + trie.search("cat")); // true
System.out.println("Search cap: " + trie.search("cap")); // true
System.out.println("Search can: " + trie.search("can")); // true
System.out.println("Search car: " + trie.search("car")); // false
System.out.println("Starts with ca: " + trie.startsWith("ca")); // true
System.out.println("Starts with bat: " + trie.startsWith("bat")); // false
}
}
How It Works
Insertion: We traverse the Trie for each character. If the character is not already present, we create a new node.
Searching: We move through the Trie following each character. If a required character is missing, the word doesn't exist.
Prefix Checking: We follow the same path as search, but don't care if it's the end of a word — just the presence of the path matters.