This project implements a Trie, also known as a Prefix Tree, in Java.
A Trie is a tree-like data structure used to store strings efficiently. It is especially useful when we need fast searching based on prefixes, such as dictionary lookup, autocomplete, spell checking, and word storage.
Trie.java
TrieUse.java
In this project, the TrieNode class is implemented together with the Trie implementation inside Trie.java.
It is not kept as a separate public class because TrieNode is only used internally by the Trie data structure. This keeps the project simple and beginner-friendly.
A Trie stores words character by character.
Each node represents one character, and paths from the root node form complete words.
For example, if we insert:
hello
hi
The words share the starting character h, so the Trie avoids storing repeated prefixes again and again.
A HashMap can also be used to store words.
For example:
HashMap<String, Boolean> map = new HashMap<>();
map.put("hello", true);
map.put("hi", true);This is good when we only want to check whether a complete word exists or not.
But a Trie is different because it stores words by characters and shared prefixes.
In a HashMap, each complete word is stored as one full key:
hello
hi
world
Searching a complete word is usually very fast, around O(1) on average.
But prefix-related operations are not natural in a HashMap.
For example, if we want all words starting with:
he
then with a HashMap, we may need to scan many stored words and check each one manually.
In a Trie, words are stored character by character:
h -> e -> l -> l -> o
|
i
So prefixes are naturally represented as paths.
If we want to find words starting with "he", we first go to the node for "he", and then print all words below that node.
Trie has an advantage over HashMap when the problem involves prefixes.
Trie is better for:
- Autocomplete systems
- Dictionary word search
- Prefix matching
- Spell checking
- Printing words in sorted order
- Finding all words with a given prefix
Example:
Words: hello, help, helper, hi
Prefix: hel
A Trie can directly reach the prefix path:
h -> e -> l
Then it can explore below that node to find:
hello
help
helper
With a HashMap, we would usually need to check every word and ask:
word.startsWith("hel")So for prefix-based tasks, Trie is more suitable and more structured.
HashMap is still better when we only need simple exact-word lookup.
For example:
map.containsKey("hello");This is usually faster and simpler than using a Trie if we do not care about prefixes.
So the simple comparison is:
| Requirement | Better Choice |
|---|---|
| Exact word search only | HashMap |
| Prefix search | Trie |
| Autocomplete | Trie |
| Sorted word traversal | Trie |
| Simple key-value storage | HashMap |
Each TrieNode stores:
char data;
TrieNode children[];
boolean isTerminal;
int childCnt;Meaning:
datastores the character of the node.childrenstores links to the next possible characters.isTerminaltells whether a complete word ends at this node.childCntstores how many children the node currently has.
The children array has size 26, so this implementation supports lowercase English letters from a to z.
The Trie class manages the complete Trie data structure.
It contains:
- A private
rootnode. - A method to add words.
- A method to search words.
- A method to remove words.
- A method to print all stored words.
trie.add("hello");The add() method inserts a word into the Trie.
It starts from the root and creates missing nodes for each character. At the end of the word, it marks the final node as terminal.
trie.search("hello");The search() method checks whether a complete word exists in the Trie.
It returns:
trueif the complete word is present.falseif the word is missing or only exists as a prefix.
For example:
trie.add("hello");
trie.search("hello"); // true
trie.search("he"); // falseHere, "he" returns false because it is only a prefix, not a complete inserted word.
trie.remove("hello");The remove() method unmarks the terminal node of the word.
After that, it also removes unnecessary nodes if:
- The node is not the end of another word.
- The node has no children.
The childCnt variable helps remove unused nodes efficiently.
trie.print();The print() method prints all complete words stored in the Trie.
It uses recursion to visit every possible path from the root.
From TrieUse.java:
public class TrieUse {
public static void main(String[] args) {
Trie trie = new Trie();
trie.add("hello");
trie.add("world");
trie.add("hi");
System.out.println(trie.search("hello")); // true
System.out.println(trie.search("world")); // true
System.out.println(trie.search("hi")); // true
System.out.println(trie.search("he")); // false
trie.print();
}
}true
true
true
false
hello
hi
world
Let L be the length of the word.
| Operation | Time Complexity |
|---|---|
| Add word | O(L) |
| Search word | O(L) |
| Remove word | O(L) |
| Print all words | O(total characters stored) |
Each add, search, and remove operation depends on the length of the word, not on the total number of words stored.
The space complexity depends on the number of unique characters stored in the Trie.
In the worst case, if no words share prefixes, more nodes are created.
If many words share prefixes, the Trie saves space by reusing common paths.
Compile:
javac Trie.java TrieUse.javaRun:
java TrieUse- Trie is useful for prefix-based word storage.
- Each node stores one character.
isTerminalmarks the end of a complete word.childCnthelps during deletion.TrieNodeis kept with theTrieimplementation inTrie.javafor simplicity.
Mrutunjaya Panda
If you found this useful, give it a ⭐ and feel free to fork!