---
date: '2024-11-18'
description: compressed prefix trie with multi-symbol edge labels and terminal markers for stored keys.
id: Radix tree
modified: 2026-10-01 09:05:21 GMT-04:00
tags:
  - technical
title: Radix tree
created: '2024-11-18'
published: '2024-11-18'
pageLayout: default
slug: thoughts/Radix-tree
permalink: https://aarnphm.xyz/thoughts/Radix-tree.md
generator:
  quartz: v4.6.0
  hostedProvider: Cloudflare
  baseUrl: aarnphm.xyz
full: https://aarnphm.xyz/llms-full.txt
---
A compressed prefix [[thoughts/university/twenty-three-twenty-four/sfwr-2c03/Hash tables|trie]]. Each edge can store several symbols, so a chain of nonterminal nodes with one child becomes a single edge. A node is _terminal_ when its root-to-node path spells a stored key. Terminal nodes may still have children.[^trie]

```jsx imports={Zoomable,PatriciaTrie}
<Zoomable label="Radix tree insertion">
  <PatriciaTrie caption="Seven keys from the Wikimedia Patricia trie figure, in its insertion order. Select a prefix length; salmon marks new edges and sage marks matched edges. The readout compares node counts; both trees store the same edge symbols." />
</Zoomable>
```

_Keys from the Patricia trie figure by Claudio Rocchini, own work, CC BY 2.5, [wikimedia](https://commons.wikimedia.org/w/index.php?curid=2118795)_

The radix counts the possible values of one branching digit. For digits of $b$ bits,

$$
r = 2^b, \qquad b \in \mathbb{N},\quad b \ge 1.
$$

Thus $r=2$ uses one bit per digit; $r=256$ uses one byte. Larger digits reduce the number of uncompressed levels and can leave more unused child slots. A compressed edge label can span several digits.[^art]

The radix bounds a node’s fan-out. The total number of internal nodes depends on the keys. For a tree with $I$ internal nodes and $L$ leaves, **if every internal node has at least two children**, counting edges gives

$$
2I \le I + L - 1
\quad\Longrightarrow\quad
I \le L - 1.
$$

A unary root or a terminal node with one child falls outside that assumption.

Take `car`, `cart`, and `cat`. The root has an edge labelled `ca`, followed by edges `r` and `t`. The `r` node stores `car` and has a further `t` edge for `cart`. Keeping its terminal marker is necessary: checking only whether the node is a leaf would reject `car`.

Looking up `cart` consumes `ca`, `r`, then `t`. Looking up `ca` reaches a nonterminal node, so it fails. Looking up `c` stops inside the first edge and also fails. These are exact-membership queries; a prefix query has a different acceptance rule.

**Lookup pseudocode**:

The root always exists, including for an empty dictionary. Outgoing labels are nonempty and have distinct first symbols. Marking the root terminal stores the empty key.

<div class="ps-root" data-inline-macros=""><span type="button" class="clipboard-button ps-clipboard" aria-label="Copy pseudocode to clipboard"><svg width="16" height="16" viewBox="0 0 16 16" class="copy-icon"><use href="#github-copy"></use></svg><svg width="16" height="16" viewBox="0 0 16 16" class="check-icon"><use href="#github-check" fill-rule="evenodd" fill="rgb(63, 185, 80)"></use></svg></span><span class="ps-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><annotation encoding="application/x-tex">"\\begin{algorithm}\n\\caption{Lookup}\n\\begin{algorithmic}\n\\State $v \\gets \\text{root}$\n\\State $i \\gets 0$\n\\While{$i &#x3C; \\text{length}(x)$}\n    \\State $e \\gets$ edge from $v$ whose label starts with $x[i]$, or null\n    \\If{$e = \\text{null}$}\n        \\State \\Return false\n    \\EndIf\n    \\If{$e.\\text{label}$ is not a prefix of $x.\\text{suffix}(i)$}\n        \\State \\Return false\n    \\EndIf\n    \\State $i \\gets i + \\text{length}(e.\\text{label})$\n    \\State $v \\gets e.\\text{targetNode}$\n\\EndWhile\n\\State \\Return $v.\\text{terminal}$\n\\end{algorithmic}\n\\end{algorithm}"</annotation></semantics></math></span>
<div class="ps-algorithm with-caption">
<p class="ps-line" style="text-indent:-0.6em;padding-left:0.6em;">
<span class="ps-keyword">Algorithm 1 </span>Lookup</p>
<div class="ps-algorithmic with-linenum">
<div class="ps-block" style="margin-left:1.2em;">
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:0em;">1:</span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mo>←</mo><mtext>root</mtext></mrow><annotation encoding="application/x-tex">v \gets \text{root}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">←</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord text"><span class="mord">root</span></span></span></span></span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:0em;">2:</span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi><mo>←</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">i \gets 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">←</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:0em;">3:</span><span class="ps-keyword">while </span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi><mo>&#x3C;</mo><mtext>length</mtext><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">i &#x3C; \text{length}(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6986em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&#x3C;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord text"><span class="mord">length</span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span><span class="ps-keyword"> do</span></p>
<div class="ps-block" style="margin-left:0.6em;">
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">4:</span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi><mo>←</mo></mrow><annotation encoding="application/x-tex">e \gets</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">e</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">←</span></span></span></span> edge from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow><annotation encoding="application/x-tex">v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span></span></span></span> whose label starts with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo stretchy="false">[</mo><mi>i</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">x[i]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mopen">[</span><span class="mord mathnormal">i</span><span class="mclose">]</span></span></span></span>, or null</p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">5:</span><span class="ps-keyword">if </span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi><mo>=</mo><mtext>null</mtext></mrow><annotation encoding="application/x-tex">e = \text{null}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">e</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord text"><span class="mord">null</span></span></span></span></span><span class="ps-keyword"> then</span></p>
<div class="ps-block" style="margin-left:0.6em;">
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-1.5em;">6:</span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-1.5em;">7:</span><span class="ps-keyword">return </span>false</p>
</div>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">8:</span><span class="ps-keyword">end if</span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">9:</span><span class="ps-keyword">if </span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi><mi mathvariant="normal">.</mi><mtext>label</mtext></mrow><annotation encoding="application/x-tex">e.\text{label}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">e</span><span class="mord">.</span><span class="mord text"><span class="mord">label</span></span></span></span></span> is not a prefix of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi mathvariant="normal">.</mi><mtext>suffix</mtext><mo stretchy="false">(</mo><mi>i</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">x.\text{suffix}(i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mord">.</span><span class="mord text"><span class="mord">suffix</span></span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mclose">)</span></span></span></span><span class="ps-keyword"> then</span></p>
<div class="ps-block" style="margin-left:0.6em;">
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-1.5em;">10:</span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-1.5em;">11:</span><span class="ps-keyword">return </span>false</p>
</div>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">12:</span><span class="ps-keyword">end if</span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">13:</span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi><mo>←</mo><mi>i</mi><mo>+</mo><mtext>length</mtext><mo stretchy="false">(</mo><mi>e</mi><mi mathvariant="normal">.</mi><mtext>label</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">i \gets i + \text{length}(e.\text{label})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">←</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7429em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord text"><span class="mord">length</span></span><span class="mopen">(</span><span class="mord mathnormal">e</span><span class="mord">.</span><span class="mord text"><span class="mord">label</span></span><span class="mclose">)</span></span></span></span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:-0.75em;">14:</span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mo>←</mo><mi>e</mi><mi mathvariant="normal">.</mi><mtext>targetNode</mtext></mrow><annotation encoding="application/x-tex">v \gets e.\text{targetNode}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">←</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">e</span><span class="mord">.</span><span class="mord text"><span class="mord">targetNode</span></span></span></span></span></p>
</div>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:0em;">15:</span><span class="ps-keyword">end while</span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:0em;">16:</span></p>
<p class="ps-line ps-code">
<span class="ps-linenum" style="left:0em;">17:</span><span class="ps-keyword">return </span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mi mathvariant="normal">.</mi><mtext>terminal</mtext></mrow><annotation encoding="application/x-tex">v.\text{terminal}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mord">.</span><span class="mord text"><span class="mord">terminal</span></span></span></span></span></p>
</div>
</div>
</div>
</div>

## complexity

For a query of $k$ symbols, lookup takes $O(k)$ time when selecting a child takes $O(1)$. Successful edge matches consume disjoint pieces of the query, and a mismatch ends the search. Compression reduces pointer traversals while retaining the symbol comparisons.

Insertion splits an edge where keys diverge. Deletion clears a terminal marker, removes an empty branch, and merges any newly nonterminal unary path. Both can take $O(k)$ with constant-time child access and labels represented by slices into stored keys. Copying long labels or scanning large child lists adds work.[^art]

A balanced comparison tree needs $O(\log n)$ _key comparisons_ for $n$ stored keys. Comparing strings from their beginnings can inspect $O(k)$ symbols each time, giving an $O(k\log n)$ worst-case lookup bound. Keep the units explicit: key comparisons and symbol comparisons count different operations.

[^trie]: James Aspnes, [Radix search](https://www.cs.yale.edu/homes/aspnes/pinewiki/RadixSearch.html), on prefix-free keys, terminal markers, and Patricia compression.

[^art]: Leis, Kemper, and Neumann, [The Adaptive Radix Tree](https://www.db.in.tum.de/~leis/papers/ART.pdf), sections III.A, III.E, and III.F, on digit width, path compression, and update operations.

