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 <= 2000word and prefix consist only of lowercase English letters.3 * 104 calls in total will be made to insert, search, and startsWith.Problem Overview: Design a Trie (Prefix Tree) that supports three operations: insert(word), search(word), and startsWith(prefix). The structure must efficiently store strings and allow fast prefix lookups.
Approach 1: Native Trie with Fixed Array Children (O(L) time per operation, O(N * A) space)
This approach builds a classic Trie where each node stores an array of 26 pointers (for lowercase English letters) and a boolean flag indicating the end of a word. During insert, iterate through each character and create nodes when a child pointer is missing. For search and startsWith, traverse the same path and verify whether nodes exist for each character. Each operation processes characters sequentially, giving O(L) time where L is the word length, while space grows with the total number of nodes allocated across all words.
The key advantage is constant-time child access using array indexing (index = char - 'a'). This removes hashing overhead and keeps traversal predictable. It is the most common implementation in interviews because the structure directly demonstrates understanding of prefix trees.
Approach 2: Trie Using Hash Maps for Children (O(L) time per operation, O(N) space)
Instead of a fixed array, each node stores its children in a hash table or dictionary keyed by characters. When inserting a word, check if the current character exists in the map; if not, allocate a new node and store it. Search and prefix queries follow the same traversal logic but perform hash lookups instead of array indexing.
This version trades slightly slower lookups for improved memory efficiency when the alphabet is large or sparse. Only existing edges are stored, which reduces unused pointers. In languages like Python or JavaScript, map-based nodes also produce cleaner implementations since dictionaries handle dynamic keys naturally.
Both implementations rely on the same core idea: a prefix tree shares common prefixes between words. Instead of storing full strings repeatedly, characters form paths from the root to terminal nodes. Prefix queries simply stop traversal early and confirm that the path exists.
Recommended for interviews: The fixed-array Trie is typically expected. It demonstrates clear understanding of prefix trees, direct character indexing, and efficient traversal. Showing the map-based version proves deeper design awareness—especially when discussing trade‑offs between memory usage and constant-time access. Both highlight how string problems can benefit from specialized data structures built around prefixes.
This approach involves creating a basic Trie data structure using a tree of nodes. Each node represents a character in a word. Starting from a root node, each character of the word is inserted sequentially, with branches representing the progression to subsequent characters. This forms a chain of nodes (linked in a tree-like manner) from the root to the nodes representing complete words. For searching, we traverse these nodes based on the characters of the input word or prefix to check for existence.
We first define a TrieNode structure with a children array to hold 26 letters and a boolean isEndOfWord to check if a node marks the end of a word. The createNode function initializes each node. TrieCreate initializes the trie by setting the root node.
The methods work by converting each character of the word or prefix into an index (0-25 for 'a'-'z'), navigating the children array of nodes for insertion or search operations. The insert function loops through each character, generating nodes along the path if needed, setting the isEndOfWord flag at the end. The search verifies both existence and termination of the string in the Trie, while startsWith logic only requires the prefix traversal.
C++
Java
Python
C#
JavaScript
Time Complexity: Insert, search, and startsWith operations are O(m), where m is the length of the word/prefix.
Space Complexity: O(26 * n * m) in the worst case, where n is the number of inserted words and m is the average length of the words. Each insertion can potentially add m nodes with 26 possible children each.
This approach relies on maps (or hashmaps) for storing children nodes dynamically. The advantage of using maps over arrays in this context arises from reduced space consumption when handling sparse trees since only existing characters are stored. Nodes manage their children using hash references, leading to more flexible branching.
This C++ implementation employs unordered_map in place of fixed-arrays to maintain child nodes in a Trie. Each TrieNode can dynamically allocate space only for characters that appear, leading to memory efficiency especially when nodes have very few immediate child nodes compared to the alphabet size. Insert, search, and startsWith operate similarly as before, now using map functionalities (find) to identify existing children and allocate if necessary.
Python
JavaScript
Time Complexity: O(m) per operation where m is the word/prefix length.
Space Complexity: O(n * m), where n estimates the word count and m accounts for varying lengths since nodes only maintain necessary mappings.
| Approach | Complexity |
|---|---|
| Native Trie Implementation | Time Complexity: Insert, search, and startsWith operations are O(m), where m is the length of the word/prefix. |
| Comprehensive Trie using Maps | Time Complexity: O(m) per operation where m is the word/prefix length. |
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Native Trie with Fixed Array Children | O(L) per operation | O(N * A) | Best for lowercase alphabet problems where constant-time character indexing is possible |
| Trie Using Hash Maps | O(L) per operation | O(N) | Useful when the alphabet is large or sparse and memory efficiency matters |
L1. Implement TRIE | INSERT | SEARCH | STARTSWITH • take U forward • 370,104 views views
Watch 9 more video solutions →Practice Implement Trie (Prefix Tree) with our built-in code editor and test cases.
Practice on FleetCodePractice this problem
Open in Editor