Skip to content
DSA tries 8 min read

Trie Operations: Insert, Search, and startsWith

A trie exposes three fundamental operations: insert a word, search for an exact word, and startsWith to test whether any stored word begins with a given prefix. All three walk the tree character by character and run in O(L) time, where L is the length of the word or prefix. This page assumes the TrieNode and Trie class from implementing a trie.

Insert

To insert a word, start at the root and walk one character at a time. For each character, follow the existing child if present, or create a new node if the path does not exist yet. After the last character, set isEnd = true to mark the word’s end.

void insert(const std::string& word) {
    TrieNode* node = root;
    for (char ch : word) {
        if (!node->children.count(ch))
            node->children[ch] = new TrieNode();
        node = node->children[ch];
    }
    node->isEnd = true;
}

Complexity: O(L) time, O(L) space in the worst case (a brand-new word with no shared prefix creates up to L nodes).

A shared helper: walk to a node

Both search and startsWith need the same first step: follow the characters of a string and return the node you land on, or “nothing” if the path breaks. It is worth isolating that walk.

TrieNode* findNode(const std::string& s) {
    TrieNode* node = root;
    for (char ch : s) {
        auto it = node->children.find(ch);
        if (it == node->children.end()) return nullptr;
        node = it->second;
    }
    return node;
}

Search (exact word)

Searching for a complete word is the walk, plus one extra check: the node you land on must have isEnd == true. Otherwise the string is only a prefix of some stored word, not a stored word itself.

bool search(const std::string& word) {
    TrieNode* node = findNode(word);
    return node != nullptr && node->isEnd;
}

Complexity: O(L) time, O(1) extra space.

Pitfall: Returning true just because the walk succeeded is the classic trie bug. If card is stored but car was never inserted, the path for car exists, yet search("car") must return false. Always check isEnd.

startsWith (prefix test)

startsWith is even simpler than search: if the path exists at all, some word goes through it, so return whether the walk reached a node. The isEnd flag is irrelevant here.

bool startsWith(const std::string& prefix) {
    return findNode(prefix) != nullptr;
}

Complexity: O(L) time, O(1) extra space.

Summary of costs

Operation       Time    Space
--------------  ------  ---------------------
insert          O(L)    O(L) new nodes worst
search          O(L)    O(1)
startsWith      O(L)    O(1)

Every cost depends only on the query length L, never on the number of stored words n — the headline advantage of a trie over scanning a hash set.

For beginners: Notice how search and startsWith share the same walk and differ by a single line. Understanding that difference — “did a word end here?” versus “does the path exist?” — is the key to tries.

Now put these to work in trie applications, or test yourself with the trie exercises.

Last updated June 25, 2026
Was this helpful?