← Wiki Index / Computation & Algorithms / Succinct Data Structure
💻 Computation & Algorithms

Succinct Data Structure

A succinct data structure stores a combinatorial object drawn from a class of size C using Z + o(Z) bits of memory—matching the information-theoretic minimum Z = log₂ C up to a sublinear additive term—while supporting fast navigation and query operations directly on the compressed representation.

Succinct data structure is a memory-optimal class of data representations within computation and algorithms that encodes a combinatorial object drawn from a finite universe of C possible configurations using Z + o(Z) bits of space, where Z = ⌈log2 C⌉ is the information-theoretic minimum number of bits required to distinguish among the C objects, while supporting navigation and query operations in constant or near-constant time without decompressing the structure. Founded by Canadian computer scientist Guy Jacobson in his 1989 doctoral thesis and paper, succinct data structures bridge the gap between Shannon information theory and pointer-based algorithmics on a Word RAM processor.

In standard pointer-based representations on a 64-bit machine, storing an unlabeled binary tree or trie of n nodes with three 64-bit pointers per node (parent, left child, right child) consumes 192n bits—or asymptotically 3n ⌈log2 n⌉ bits—even though the number of distinct binary trees on n nodes is the n-th Catalan number Cn = (1 / (n + 1)) · (2n choose n), whose information-theoretic lower bound is only log2(Cn) = 2n − Θ(log n) bits. Succinct data structures eliminate this Θ(log n) pointer overhead by replacing explicit memory addresses with bitvectors augmented by sublinear o(n)-bit Rank and Select index directories.

Information-Theoretic Space Hierarchy

Let U be a class of combinatorial objects of size |U| = C (for example, all subsets of size k from {1, …, n}, all rooted ordinal trees on n nodes, or all strings of length n over an alphabet of size σ). By the pigeonhole principle, any worst-case binary encoding that uniquely represents every member of U must use at least Z = ⌈log2 C⌉ bits in the worst case. Space-efficient data structures are classified by how their memory consumption compares to this lower bound Z:

  1. Implicit Data Structure (Z + O(1) bits): Uses only the raw array of elements plus a constant number of machine words (for example, a standard binary heap stored in an array A[1 .. n] where children of node i are at 2i and 2i + 1).
  2. Succinct Data Structure (Z + o(Z) bits): Uses Z bits for the canonical representation plus a sublinear additive redundancy o(Z) bits (for example, O(Z / log log Z) or O(Z log log Z / log Z) bits) to support fast queries.
  3. Compact Data Structure (O(Z) bits): Uses at most c · Z bits for a small constant c > 1, trading a constant-factor space overhead for simpler indexing.
  4. Compressed / Opportunistic Data Structure (Hk + o(Z) bits): Exploits the empirical statistical entropy H0 or k-th order empirical entropy Hk of the specific instance stored (as in the FM-index, wavelet tree, or Raman–Raman–Rao compressed bitvector) to use even fewer than Z bits when the input is non-uniform.

Bitvector Primitives: Rank and Select in O(1) Time

Definitions of Access, Rank, and Select

Virtually every succinct tree, graph, and string index reduces its pointer navigation to three constant-time primitives over a static bitvector B[0 .. n − 1] ∈ {0, 1}n (whose information-theoretic size is Z = n bits):

  • Access(B, i): Returns the bit B[i] at index i ∈ [0, n − 1].
  • Rankb(B, i): Returns the number of occurrences of bit b ∈ {0, 1} in the prefix B[0 .. i]. Notice that Rank0(B, i) = (i + 1) − Rank1(B, i), so it suffices to index Rank1.
  • Selectb(B, j): Returns the index i of the j-th occurrence of bit b ∈ {0, 1} in B (that is, the smallest index i such that Rankb(B, i) = j).

Jacobson's Two-Level Directory for Constant-Time Rank

Guy Jacobson (1989) and David R. Clark and J. Ian Munro (1996) showed how to answer Rank1(B, i) in O(1) time using only o(n) auxiliary bits on top of the raw bitvector B via a three-tier Four Russians decomposition:

  1. Superblocks (Length s = ⌊log22 n⌋): Partition B into n / s superblocks of length s. For each superblock boundary k, store the cumulative 1-bit count Rank1(B, k · s − 1) in a table Rsuper. Because each entry requires ⌈log2 n⌉ bits, Rsuper occupies (n / log22 n) · log2 n = O(n / log n) = o(n) bits.
  2. Mini-Blocks (Length b = ⌊(1/2) log2 n⌋): Subdivide each superblock into s / b = 2 log2 n mini-blocks of length b. For each mini-block, store its cumulative 1-bit count relative to the start of its enclosing superblock in a table Rmini. Since the relative count inside a superblock is at most s = log22 n, each entry requires only ⌈log2(log22 n)⌉ = 2 log2 log2 n bits! Across all n / b mini-blocks, Rmini occupies:
Space(Rmini) = (2n / log2 n) · (2 log2 log2 n) = O((n log log n) / log n) = o(n) bits
  1. Intra-Block Popcount / Four Russians Table: The remaining prefix inside the final mini-block has length at most b = (1/2) log2 n bits, which fits inside a single machine word and can be counted in O(1) time either via a shared Four Russians lookup table of size 2b · b · log2 b = O(√n log n log log n) = o(n) bits, or on modern CPUs via a single hardware POPCNT instruction after bit-masking!

Adding the superblock entry, mini-block entry, and masked intra-block popcount yields Rank1(B, i) in 3 array accesses and O(1) time. Clark and Munro (1996) extended this variable-density block partitioning (splitting sparse vs. dense 1-bit intervals) to support Select1(B, j) in O(1) worst-case time using O(n / log log n) = o(n) bits, and Mihai Pătraşcu (2008) reduced the redundancy of static rank/select bitvectors to O(n / logc n) bits for any constant c.

Raman–Raman–Rao (RRR) Zero-Order Entropy Bitvectors

When a bitvector of length n is sparse (containing only m ≪ n ones), there are only (n choose m) possible bitvectors, so the information-theoretic lower bound drops to B0(n, m) = ⌈log2(n choose m)⌉ ≈ m log2(n/m) + 1.44 m bits. Rajeev Raman, Venkatesh Raman, and S. Srinivasa Rao (2002) designed the RRR bitvector, which partitions B into blocks of length b = (1/2) log2 n and stores each block as a pair (class ci, offset oi), where the class ci ∈ {0, …, b} is the number of 1-bits in the block and the offset oi ∈ {0, …, (b choose ci) − 1} indexes the specific bit combination of weight ci. Summing ⌈log2(b choose ci)⌉ across all blocks compresses the bitvector to B0(n, m) + o(n) bits while preserving O(1) Access, Rank, and Select.

Succinct Representations of Ordinal Trees (2n + o(n) Bits)

An ordinal tree on n nodes is an arbitrary rooted tree in which the children of each node are ordered from left to right. Because there are Cn ≈ 4n / (√π n3/2) distinct ordinal trees on n nodes, any succinct tree representation must use 2n + o(n) bits. Three canonical bitvector encodings achieve this bound while supporting rich navigation primitives in O(1) time:

  1. LOUDS (Level-Order Unary Degree Sequence — Jacobson, 1989): Visits nodes in breadth-first (level) order. Each node with d children is encoded in unary as d ones followed by a single zero (1d0), prefixed by a dummy super-root 10. The resulting bitvector has length 2n + 1 bits. Using Rank and Select:
    • The k-th child of the node whose 1d0 block starts at position p corresponds to the (p + k − 1)-th 1-bit in the bitvector, whose own degree block begins right after Select0(Rank1(p + k − 1))!
    • Thus Parent(v), First-Child(v), Next-Sibling(v), Child(v, k), and Degree(v) all execute in O(1) time.
  2. BP (Balanced Parentheses — Munro and Raman, 2001): Performs a depth-first preorder traversal of the tree, writing an opening parenthesis "(" (bit 1) when first entering a node and a closing parenthesis ")" (bit 0) when exiting its subtree, producing a balanced sequence of length 2n bits. Equipped with FindClose, FindOpen, and Enclose directories of o(n) bits, BP supports not only Parent and First-Child in O(1) time, but also Subtree-Size(v) = (FindClose(v) − v + 1) / 2, Depth(v), Ancestor tests, Level-Ancestor(v, d), and range minimum queries / LCA(u, v) in O(1) time!
  3. DFUDS (Depth-First Unary Degree Sequence — Benoit et al., 2005): Combines the fast random child access Child(v, k) of LOUDS with the subtree-locality of BP by writing each node's unary degree "("d")" in depth-first preorder rather than level order, supporting both Child(v, k) and Subtree-Size(v) in O(1) time inside 2n + o(n) bits.
Succinct Tree RepresentationSpace (Bits)Parent / First-Childk-th Child Child(v, k)Subtree-Size(v)LCA(u, v) / Depth(v)
Pointer Tree (3 × 64-bit words)192n bitsO(1)O(k) or O(1)Requires extra fieldRequires extra structures
LOUDS (Jacobson, 1989)2n + o(n)O(1)O(1)Not supported in O(1)Not supported in O(1)
Balanced Parentheses (BP, 2001)2n + o(n)O(1)O(1) (via range min-max)O(1)O(1)
DFUDS (Benoit et al., 2005)2n + o(n)O(1)O(1)O(1)O(1)

Genomics, Text Indexing, and Wavelet Trees

Succinct data structures power whole-genome alignment and modern bioinformatics software (such as Bowtie2, BWA, and SDSL). Storing a pointer-based suffix tree of the 3.1-gigabase human reference genome requires over 45 GB of RAM, whereas Ferragina and Manzini's FM-index (2000) combines the Burrows–Wheeler Transform (BWT) of the genome with a succinct wavelet tree over the 4-letter DNA alphabet {A, C, G, T} (2 bits per nucleotide) and sampled suffix array checkpoints, compressing the full-text searchable index of the entire human genome into under 1.5 GB of RAM while supporting backward pattern matching in O(|P|) Rank queries.

Frequently Asked Questions

Why is an o(Z) additive term required instead of 0 extra bits?

An implicit data structure using Z + O(1) bits can encode an object without extra memory, but scanning variable-length fields or counting prefix bits up to position i in a raw bitvector without auxiliary checkpoints would require Ω(n / w) word reads. Storing a sublinear o(Z) directory of cumulative checkpoints at superblock and mini-block boundaries allows queries to jump directly to the target neighborhood in O(1) memory accesses while ensuring that the relative overhead limZ → ∞ (o(Z) / Z) = 0 vanishes asymptotically as the dataset grows.

How does a wavelet tree generalize bitvector Rank and Select to larger alphabets?

Given a string S[0 .. n − 1] over an alphabet Σ of size σ, a wavelet tree (Grossi, Gupta, and Vitter, 2003) recursively splits the alphabet in half at each level of a balanced binary tree of height ⌈log2 σ⌉, storing a succinct bitvector at each level indicating whether each character belongs to the lower or upper half of the alphabet. This reduces character Access, Rankc(S, i), and Selectc(S, j) over arbitrary alphabets to log2 σ bitvector Rank/Select queries in n log2 σ + o(n log σ) bits (or n H0(S) + o(n log σ) bits when built with RRR bitvectors or Huffman shape).

Are dynamic succinct data structures possible?

Yes. By replacing static superblocks and mini-blocks with a balanced B-tree whose leaves store short bitvector chunks of length Θ(log2 n) bits and whose internal nodes maintain subtree bit-length and 1-bit population counts, dynamic succinct bitvectors and trees support bit Insert(B, i, b) and Delete(B, i) alongside Rank and Select in O(log n / log log n) time within Z + o(Z) bits (Navarro and Sadakane, 2014).

References

  1. Jacobson, G. (1989). "Space-efficient static trees and graphs." In Proceedings of the 30th Annual Symposium on Foundations of Computer Science (SFCS 1989), 549–554. https://doi.org/10.1109/SFCS.1989.63533
  2. Munro, J. I., & Raman, V. (2001). "Succinct representation of balanced parentheses and static trees." SIAM Journal on Computing, 31(3), 762–776. https://doi.org/10.1137/S0097539799364092
  3. Raman, R., Raman, V., & Rao, S. S. (2007). "Succinct indexable dictionaries with applications to encoding k-ary trees, prefix sums and multisets." ACM Transactions on Algorithms, 3(4), Article 43. https://doi.org/10.1145/1290672.1290680
  4. Benoit, D., Demaine, E. D., Munro, J. I., Raman, R., Raman, V., & Rao, S. S. (2005). "Representing trees of higher degree." Algorithmica, 43(4), 275–292. https://doi.org/10.1007/s00453-004-1146-6
  5. Ferragina, P., & Manzini, G. (2005). "Indexing compressed text." Journal of the ACM, 52(4), 552–581. https://doi.org/10.1145/1082036.1082039
🧬 Support Open Science
Explore interactive science games on LowKey Games or browse scientific apparel & accessories in the CRV Science Shop.
💡 Spot an inaccuracy or have primary literature to expand this article?
Submit a citation or revision note using your shared CRV Science / LowKey Games profile ID.
Suggest Revision