Trie


In computer science, a trie, also known as a digital tree or prefix tree, is a specialized search tree data structure used to store and retrieve strings from a dictionary or set. Unlike a binary search tree, nodes in a trie do not store their associated key. Instead, each node's position within the trie determines its associated key, with the connections between nodes defined by individual characters rather than the entire key.
Tries are particularly effective for tasks such as autocomplete, spell checking, and IP routing, offering advantages over hash tables due to their prefix-based organization and lack of hash collisions. Every child node shares a common prefix with its parent node, and the root node represents the empty string. While basic trie implementations can be memory-intensive, various optimization techniques such as compression and bitwise representations have been developed to improve their efficiency. A notable optimization is the radix tree, which provides more efficient prefix-based storage.
While tries store character strings, they can be adapted to work with any ordered sequence of elements, such as permutations of digits or shapes. A notable variant is the bitwise trie, which uses individual bits from fixed-length binary data as keys.

History, etymology, and pronunciation

The idea of a trie for representing a set of strings was first abstractly described by Axel Thue in 1912. Tries were first described in a computer context by René de la Briandais in 1959.
The idea was independently described in 1960 by Edward Fredkin, who coined the term trie, pronouncing it , after the middle syllable of retrieval. However, other authors pronounce it , in an attempt to distinguish it verbally from "tree".

Overview

Tries are a form of string-indexed look-up data structure, which is used to store a dictionary list of words that can be searched on in a manner that allows for efficient generation of completion lists. A prefix trie is an ordered tree data structure used in the representation of a set of strings over a finite alphabet set, which allows efficient storage of words with common prefixes.
Tries can be efficacious on string-searching algorithms such as predictive text, approximate string matching, and spell checking in comparison to binary search trees. A trie can be seen as a tree-shaped deterministic finite automaton.

Operations

Tries support various operations: insertion, deletion, and lookup of a string key. Tries are composed of nodes that contain links, which either point to other suffix child nodes or null. As for every tree, each node except the root is pointed to by only one other node, called its parent. Each node contains as many links as the number of characters in the applicable alphabet. In some cases, the alphabet used is simply that of the character encoding—resulting in, for example, a size of 128 in the case of ASCII.
The null links within the children of a node emphasize the following characteristics:
  1. Characters and string keys are implicitly stored in the trie, and include a character sentinel value indicating string termination.
  2. Each node contains one possible link to a prefix of strong keys of the set.
A basic structure type of nodes in the trie is as follows: may contain an optional, which is associated with the key that corresponds to the node.

Searching

Searching for a value in a trie is guided by the characters in the search string key, as each node in the trie contains a corresponding link to each possible character in the given string. Thus, following the string within the trie yields the associated value for the given string key. A null link during the search indicates the inexistence of the key.
The following pseudocode implements the search procedure for a given string in a rooted trie.
In the above pseudocode, and correspond to the pointer of the trie's root node and the string key, respectively. The search operation takes time, where is the size of the string parameter. In a balanced binary search tree, on the other hand, it takes time, in the worst case, since needs to be compared with other keys and each comparison takes time, in the worst case.
The trie occupies less space, in comparison with a binary search tree, in the case of a large number of short strings, since nodes share common initial string subsequences and store the keys implicitly.

Insertion

Insertion into trie is guided by using the character sets as indexes to the children array until the last character of the string key is reached. Each node in the trie corresponds to one call of the radix sorting routine, as the trie structure reflects the execution of pattern of the top-down radix sort.
If null links are encountered before reaching the last character of the string key, new nodes are created. The input value is assigned to the value of the last node traversed, which is the node that corresponds to the key.

Deletion

Deletion of a key–value pair from a trie involves finding the node corresponding to the key, setting its value to null, and recursively removing nodes that have no children.
The procedure begins by examining ; an empty string indicates arrival at the node corresponding to the key, in which case its value is set to null. If the node, then, has null value and no children, it is removed from the trie by returning null; otherwise, the node is kept by returning the node itself.

Replacing other data structures

Replacement for hash tables

A trie can be used to replace a hash table, over which it has the following advantages:
  • Searching for a node with an associated key of size has the complexity of, whereas an imperfect hash function may have numerous colliding keys, and the worst-case lookup speed of such a table would be, where denotes the total number of nodes within the table.
  • Tries do not need a hash function for the operation, unlike a hash table; there are also no collisions of different keys in a trie.
  • Within a trie, keys can be efficiently sorted lexicographically.
However, tries are less efficient than a hash table when the data is directly accessed on a secondary storage device such as a hard disk drive that has higher random access time than the main memory.

Implementation strategies

Tries can be represented in several ways, corresponding to different trade-offs between memory use and speed of the operations. Using a vector of pointers for representing a trie consumes enormous space; however, memory space can be reduced at the expense of running time if a singly linked list is used for each node vector, as most entries of the vector contains.
Techniques such as alphabet reduction may reduce the large space requirements by reinterpreting the original string as a longer string over a smaller alphabet. For example, a string of bytes can alternatively be regarded as a string of four-bit units. This can reduce memory usage by a factor of eight; but lookups need to visit twice as many nodes in the worst case. Another technique includes storing a vector of 256 ASCII pointers as a bitmap of 256 bits representing ASCII alphabet, which reduces the size of individual nodes dramatically.

Bitwise tries

Bitwise tries are used to address the enormous space requirement for the trie nodes in a naive simple pointer vector implementations. Each character in the string key set is represented via individual bits, which are used to traverse the trie over a string key. The implementations for these types of trie use vectorized CPU instructions to find the first set bit in a fixed-length key input. Accordingly, the set bit is used to index the first item, or child node, in the 32- or 64-entry based bitwise tree. Search then proceeds by testing each subsequent bit in the key.
This procedure is also cache-local and highly parallelizable due to register independency, and thus performant on out-of-order execution CPUs.

Compressed tries

, also known as a compressed trie, is a space-optimized variant of a trie in which any node with only one child gets merged with its parent; elimination of branches of the nodes with a single child results in better metrics in both space and time. This works best when the trie remains static and set of keys stored are very sparse within their representation space.
One more approach for static tries is to "pack" the trie by storing disjoint sets of children in the same memory location, interleaved.

Patricia trees

Patricia trees are a particular implementation of the compressed binary trie that uses the binary encoding of the string keys in its representation. Every node in a Patricia tree contains an index, known as a "skip number", that stores the node's branching index to avoid empty subtrees during traversal. A naive implementation of a trie consumes immense storage due to larger number of leaf-nodes caused by the sparse distribution of keys; Patricia trees can be efficient for such cases.
A representation of a Patricia tree is shown to the right. Each index value adjacent to the nodes represents the "skip number"—the index of the bit with which branching is to be decided. The skip number 1 at node 0 corresponds to the position 1 in the binary encoded ASCII where the leftmost bit differed in the key set. The skip number is crucial for search, insertion, and deletion of nodes in the Patricia tree, and a bit masking operation is performed during every iteration.

Applications

Trie data structures are commonly used in predictive text or autocomplete dictionaries, and approximate matching algorithms. Tries enable faster searches, occupy less space, especially when the set contains large number of short strings, thus used in spell checking, hyphenation applications and longest prefix match algorithms. However, if storing dictionary words is all that is required, a minimal deterministic acyclic finite state automaton or radix tree would use less storage space than a trie. This is because DAFSAs and radix trees can compress identical branches from the trie that correspond to the same suffixes of different words being stored. String dictionaries are also utilized in natural language processing, such as finding lexicon of a text corpus.