CASE STUDY 01 · ADVANCED DATA STRUCTURES

Advanced Trie + Ranking

Smart Search Autocomplete

Turn a partial word into useful suggestions while preserving shared prefixes, tracking popularity and removing terms without breaking other words.

01 · PROBLEM DEFINITION

A prefix can represent many intentions

Suppose a learner types gr in the CodeBhavya search box. The system should quickly return terms such as graph and greedy. A useful result is not merely alphabetical: frequently selected terms should appear first, while equal-frequency terms need a deterministic tie-break.

The dictionary changes over time. Administrators can add or remove terms, and each selection raises that word’s popularity score. The system therefore needs efficient prefix navigation, shared storage and safe updates.

Scope: The teaching implementation accepts English alphabetic words and normalises them to lowercase. A production search engine would also handle spaces, Unicode, misspellings, personalization and persistent storage.

Query

Locate the node represented by the typed prefix.

Explore

Enumerate complete words below that node.

Rank

Prefer higher frequency, then alphabetical order.

02 · REQUIREMENTS

Translate the product idea into contracts

OperationInputRequired result
Add termAlphabetic wordCreate only missing prefix nodes and mark its final node
SuggestValid prefixReturn at most five best matching words
Record selectionExisting complete wordIncrease only that word’s frequency
RemoveExisting complete wordUnmark it and prune nodes that no other word requires
ExitNoneRelease every allocated Trie node

Boundary cases

  • A query whose path does not exist must return no suggestions.
  • A prefix may itself be a stored word, such as art before article.
  • Deleting trie must not delete a shared prefix required by trigger.
  • Repeated insertion must update the terminal score without duplicating the word.
  • Uppercase input is normalised; digits and punctuation are rejected by this version.
03 · STRUCTURE SELECTION

Why a Trie matches prefix search

TrieNode = { child[26], isWord, frequency }
root → g → r → a → p → h*
             ↘ e → e → d → y*

Each edge represents a character. Words with the same beginning share the same path, so the prefix is processed once. After reaching the prefix node, depth-first traversal discovers only words inside that prefix subtree.

CandidatePrefix lookupUpdateTrade-off
Unsorted arrayO(nL)Simple appendEvery query scans every word
Sorted arrayO(L log n) searchCostly movementCompact and cache-friendly
Hash tableNot naturally supportedExpected O(L)Strong for exact lookup
TrieO(P) to prefix nodeO(L)Chosen; faster prefixes but many pointers

Here, L is word length and P is prefix length. Collecting matches still costs time proportional to the explored subtree; a Trie does not make result generation free.

04 · CORRECTNESS INVARIANTS

Rules that keep the dictionary trustworthy

  1. Every child index is derived from a lowercase letter: word[i] - 'a' lies from 0 through 25.
  2. A path can exist without representing a word; only isWord == 1 makes it a valid result.
  3. A frequency belongs to the terminal node of exactly one complete word.
  4. Deletion may free a node only when it is non-terminal and has no children.
  5. The root is never removed by a word-deletion operation.
  6. Every node reachable from root is released exactly once during final cleanup.
Common deletion error: Removing every node on a word’s path can erase longer words or words sharing the prefix. Pruning must travel back from the final character and stop as soon as a node is still needed.
05 · ALGORITHM DESIGN

Search is navigation followed by controlled exploration

Autocomplete pipeline

  1. Normalise and validate the prefix.
  2. Follow one child pointer per character. Stop if any required pointer is NULL.
  3. Copy the prefix into a working buffer.
  4. Run DFS from the prefix node. Whenever isWord is true, save the buffer and frequency.
  5. Sort collected candidates by descending frequency and then ascending word.
  6. Display the first five entries.

Recursive deletion

The base case unmarks the terminal node and clears its score. As recursion returns, a child reports whether it became unnecessary. Its parent frees that child and clears the pointer. The return value is therefore not “word removed”; it means “this node may now be pruned.” A separate flag records whether the requested complete word existed.

Find terminal
Unmark word
Return upward
Prune unused suffix
06 · COMPLETE IMPLEMENTATION

Compiler-ready C11 program

programs/smart-autocomplete.c
Open Compiler
Loading source…

Implementation reading order

Foundation

Start with createNode, normalization and insertion.

Query

Follow findPrefixNode, DFS collection and ranking.

Ownership

Finish with recursive deletion and freeTrie.

The fixed suggestion array keeps the example approachable. For an unbounded dictionary, use a dynamically growing vector or maintain top suggestions at each Trie node.

07 · INTERACTIVE TRACING

Trace gr and its ranked results

  1. Begin with prefix gr and follow character g.
  2. Follow character r.
  3. Explore the gra branch.
  4. Explore the gre branch.
  5. Finish collecting.
  6. Apply ranking comparator.
  7. Return up to five results.
Current state

Press Next to begin.

08 · TEST DESIGN

Test shared paths, ranking and invalid input

Test 1 — Existing prefix
Query gr. The sample data must return graph before greedy because 22 is greater than 11.
Test 2 — Ranking update
Select greedy repeatedly until its score exceeds graph, then query gr. Greedy must move to first position without modifying either spelling.
Test 3 — Prefix is a word
Add art and article, then query art. Both must appear; the terminal marker distinguishes the shorter complete word from an ordinary internal node.
Test 4 — Safe deletion
Add code and coder, remove code, then query cod. Coder must remain reachable while code stops appearing as a result.
Test 5 — Rejection and cleanup
Reject c99 without changing the Trie. Exit after many additions and verify with AddressSanitizer that no allocated nodes remain.
09 · COMPLEXITY

State the cost with the missing variables included

OperationTimeExtra spaceExplanation
Insert / exact navigationO(L)O(L) new nodes worst caseOne edge per character
Reach prefix nodeO(P)O(1)Follow prefix path
Collect matchesO(S)O(H + R)Explore S subtree nodes; recursion height H; R results
Rank R matchesO(R log R)Depends on sortComparison sorting
Delete wordO(L · A)O(L)Current hasChildren scans alphabet A=26 at return steps

A direct child array makes transitions O(1) but consumes 26 pointers in every node. A sparse map of children saves memory when branching is low, at the cost of slower or more complex access.

10 · PRACTICE

Check the reasoning

The path c-o-d-e exists. What proves that “code” is a valid result?

When must recursive deletion stop pruning?

Extension challenges

  1. Support phrases containing spaces while preserving safe character indexing.
  2. Use a size-five min-heap so ranking does not sort every candidate.
  3. Store the top five descendants at each Trie node and explain update costs.
  4. Add edit-distance suggestions for one-character typing errors.
  5. Persist the dictionary and scores in a file between sessions.
11 · INTERVIEW PREPARATION

Explain the trade-offs, not just the code

Why can a Trie be faster than scanning all strings?

It navigates directly to the prefix node in O(P). Only that node’s descendants can match, so unrelated dictionary regions are skipped.

Why is autocomplete not simply O(P)?

O(P) reaches the prefix node. Producing suggestions additionally explores matching descendants and may rank many results. Output-sensitive work must be included.

How can memory usage be reduced?

Replace the 26-pointer array with a compact child list or map, compress single-child chains into radix edges, or use a ternary search tree.

How would a large service avoid sorting every query?

Cache a small ranked candidate list at each prefix node. Reads become fast, while insertions and popularity updates must repair caches along the word’s path.

Is popularity alone a sufficient ranking signal?

No. A real system may combine global popularity, recency, spelling quality, context and user preferences, with privacy and fairness constraints.

12 · KEY TAKEAWAY

Fast navigation and useful ranking are separate concerns

The Trie narrows the search space by structure; it does not decide which match is best. A complete design separates prefix retrieval, candidate collection, ranking policy and memory ownership so each can be tested independently.