|
Zip and Skip Tries
|
Implements a zip trie, a data structure combining properties of tries and zip trees for storing strings efficiently. More...
#include <ZipTrie.hpp>

Classes | |
| struct | AncestorLCPs |
| Stores the Longest Common Prefix (LCP) values associated with the path from the root down to a node's parent. More... | |
| struct | Bucket |
| Represents a single node (bucket) within the ZipTrie's internal storage. More... | |
| struct | ComparisonResult |
Holds the result of comparing a search/insert key x with a node v's key, considering the context of ancestor LCPs. More... | |
| struct | SearchResults |
Holds the results returned by a search operation (search method). More... | |
Public Types | |
| using | LCP_T = std::conditional_t< MEMORY_EFFICIENT, MemoryEfficientLCP, unsigned > |
| The type used to store Longest Common Prefix values. | |
| using | KEY_T = BitString< CHAR_T, CHAR_SIZE_BITS > |
The type used for keys, an instantiation of the BitString template. | |
Public Member Functions | |
| ZipTrie (unsigned max_size, unsigned max_lcp_length) noexcept | |
| Constructs an empty ZipTrie. | |
| int | get_depth (const KEY_T *key) const noexcept |
| Calculates the depth of a given key in the trie. | |
| int | height () const noexcept |
| Calculates the height of the trie. | |
| double | get_average_height () const noexcept |
| Calculates the average depth of all nodes currently in the trie. | |
| unsigned | size () const noexcept |
| Returns the number of nodes (keys) currently stored in the trie. | |
| bool | contains (const KEY_T *key) const noexcept |
| Checks if a key exists within the trie. | |
| LCP_T | lcp (const KEY_T *key) const noexcept |
| Finds the Longest Common Prefix (LCP) between the search key and the path taken in the trie. | |
| void | insert (const KEY_T *key) noexcept |
| Inserts a key into the zip trie. | |
| const RANK_T & | get_root_rank () const noexcept |
| Removes a node with a given key from the zip tree. (Not implemented) | |
| void | set (const KEY_T *key, RANK_T rank, unsigned left, unsigned right, LCP_T predecessor_lcp, LCP_T successor_lcp) noexcept |
| Directly adds a pre-constructed bucket to the internal storage vector. | |
| void | set_root_index (unsigned root_index) noexcept |
| Directly sets the root index of the trie. | |
| virtual SearchResults | search (const KEY_T *key) const noexcept |
| Performs a search for a key within the ZipTrie. | |
| void | to_dot (const std::string &file_path) const noexcept |
| Generates a DOT language representation of the trie structure for visualization. | |
Protected Member Functions | |
| MemoryEfficientLCP | get_memory_efficient_lcp (size_t num) const noexcept |
Converts a standard LCP length (size_t) into the MemoryEfficientLCP format. | |
| template<typename CompareFunction > | |
| SearchResults | search_recursive (const KEY_T *key, CompareFunction compare_func) const noexcept |
Recursive helper function for the public search method. | |
| template<typename CompareFunction > | |
| unsigned | insert_recursive (Bucket *x, unsigned x_index, unsigned v_index, AncestorLCPs ancestor_lcps, CompareFunction compare_func) noexcept |
Recursive helper function for inserting a new node x. | |
| std::optional< ComparisonResult > | k_compare_prefix_check (const Bucket &v, const AncestorLCPs &ancestor_lcps, size_t &out_start_lcp_val) const noexcept |
| Performs an initial check based on ancestor LCPs before full key comparison (k-compare optimization). | |
| LCP_T | convert_lcp (size_t val) const noexcept |
Converts an exact LCP length (e.g., number of matching characters/bits) to the LCP_T type. | |
Protected Attributes | |
| unsigned | _root_index |
Index of the root node within the _buckets vector. Initialized to NULLPTR for an empty trie. | |
| unsigned | _max_lcp_length |
| Stores the maximum possible LCP length provided during construction. | |
| uint8_t | _log_max_size |
Stores the approximate log base 2 of the max_size provided during construction. | |
| std::vector< Bucket > | _buckets |
A contiguous vector storing all the nodes (Buckets) of the trie. | |
Static Protected Attributes | |
| static constexpr unsigned | NULLPTR = std::numeric_limits<unsigned>::max() |
Represents a null child pointer index using the maximum value of unsigned. | |
Private Member Functions | |
| ComparisonResult | k_compare (const KEY_T *x, const Bucket &v, const AncestorLCPs &ancestor_lcps) const noexcept |
Compares a key x with the key in a node v, utilizing ancestor LCPs for optimization (k-compare). | |
| int | height (unsigned node_index) const noexcept |
Recursive helper function to calculate the height of the subtree rooted at node_index. | |
| uint64_t | get_total_depth (unsigned node_index, uint64_t depth) const noexcept |
| Recursive helper function to calculate the sum of the depths of all nodes within a subtree. | |
| void | to_dot_recursive (std::ofstream &os, unsigned node_index) const noexcept |
| Recursive helper function to generate the DOT language representation for a subtree. | |
Implements a zip trie, a data structure combining properties of tries and zip trees for storing strings efficiently.
A zip trie stores keys (represented by BitString) and associated ranks (RANK_T). It maintains a tree structure based on both key prefixes (like a standard trie) and random ranks (like a zip tree). This combination aims to achieve balanced expected depth (logarithmic in the number of elements) while efficiently handling shared prefixes common in string datasets.
The structure relies on comparing keys based on their Longest Common Prefix (LCP) with ancestor paths (k_compare optimization) to navigate the trie efficiently, avoiding redundant character comparisons. 'Zip' operations based on node ranks ensure the max-heap property (the "zip property") is maintained with respect to ranks, leading to the balanced structure on average.
| CHAR_T | The underlying character type used in the keys (BitString). Typically char or uint8_t. |
| MEMORY_EFFICIENT | If true, uses MemoryEfficientLCP for storing LCP values, trading precision for memory savings. If false, uses unsigned for exact LCP storage. |
| RANK_T | The type used for node ranks (default: GeometricRank). Must provide a static get_random() method and support comparison operators (<=>). |
| CHAR_SIZE_BITS | The number of significant bits per character in CHAR_T. Defaults to sizeof(CHAR_T) * 8. Allows handling types where not all bits are used (e.g., 7-bit ASCII in an 8-bit char, 2-bit nucleotides in DNA strings). |
Definition at line 125 of file ZipTrie.hpp.
| using ZipTrie< CHAR_T, MEMORY_EFFICIENT, RANK_T, CHAR_SIZE_BITS >::KEY_T = BitString<CHAR_T, CHAR_SIZE_BITS> |
The type used for keys, an instantiation of the BitString template.
Definition at line 137 of file ZipTrie.hpp.
| using ZipTrie< CHAR_T, MEMORY_EFFICIENT, RANK_T, CHAR_SIZE_BITS >::LCP_T = std::conditional_t<MEMORY_EFFICIENT, MemoryEfficientLCP, unsigned> |
The type used to store Longest Common Prefix values.
This is MemoryEfficientLCP if MEMORY_EFFICIENT is true, otherwise unsigned.
Definition at line 132 of file ZipTrie.hpp.
|
noexcept |
Constructs an empty ZipTrie.
| max_size | Hint for reserving memory in the internal node storage (_buckets). Pre-allocating can potentially avoid reallocations during insertions if the size is known approximately. |
| max_lcp_length | The maximum possible LCP length (in characters or bits, depending on BitString) expected between any two keys. This is crucial for the scaling of MemoryEfficientLCP if enabled. |
max_lcp_length accurately is important for the precision of approximate LCPs when MEMORY_EFFICIENT is true. _log_max_size based on max_size for LCP scaling. Definition at line 558 of file ZipTrie.hpp.
|
noexcept |
Checks if a key exists within the trie.
| key | Pointer to the key to search for. |
Definition at line 569 of file ZipTrie.hpp.
|
inlineprotectednoexcept |
Converts an exact LCP length (e.g., number of matching characters/bits) to the LCP_T type.
If MEMORY_EFFICIENT is true, this calls get_memory_efficient_lcp to perform the approximation. Otherwise (if MEMORY_EFFICIENT is false), it performs a simple static cast to unsigned (which is LCP_T).
| val | The exact LCP length (size_t) to convert. |
MemoryEfficientLCP or unsigned). MEMORY_EFFICIENT. Definition at line 636 of file ZipTrie.hpp.
|
noexcept |
Calculates the average depth of all nodes currently in the trie.
This provides a measure of the average search cost. A perfectly balanced binary tree would have an average depth logarithmic in the number of nodes.
Definition at line 896 of file ZipTrie.hpp.
|
noexcept |
Calculates the depth of a given key in the trie.
The depth is the number of edges from the root to the node containing the key. The root itself is at depth 0.
| key | Pointer to the key to search for. |
Definition at line 888 of file ZipTrie.hpp.
|
inlineprotectednoexcept |
Converts a standard LCP length (size_t) into the MemoryEfficientLCP format.
This is an internal helper function used only when MEMORY_EFFICIENT is true. It approximates the LCP value to fit into the smaller MemoryEfficientLCP struct, using _log_max_size for scaling the approximation.
| num | The exact LCP length (typically number of matching characters or bits) to convert. |
MEMORY_EFFICIENT is true. Definition at line 928 of file ZipTrie.hpp.
|
noexcept |
Removes a node with a given key from the zip tree. (Not implemented)
| key | The key of the node to remove. |
Gets the rank of the root node.
_root_index == NULLPTR). Accessing _buckets[_root_index] would be invalid. Definition at line 1031 of file ZipTrie.hpp.
|
privatenoexcept |
Recursive helper function to calculate the sum of the depths of all nodes within a subtree.
Used by get_average_height. Traverses the subtree and sums the depth of each node.
| node_index | Index of the root of the subtree. |
| depth | The depth of the node_index node itself (relative to the overall trie root). |
node_index. Each node's depth contributes to the sum. Returns 0 for an empty subtree (NULLPTR). Definition at line 912 of file ZipTrie.hpp.
|
noexcept |
Calculates the height of the trie.
The height is the number of edges on the longest path from the root node to any leaf node. An empty trie has height -1. A trie with only a root node has height 0.
Definition at line 867 of file ZipTrie.hpp.
|
privatenoexcept |
Recursive helper function to calculate the height of the subtree rooted at node_index.
| node_index | Index of the root of the subtree in the _buckets vector. |
node_index to a leaf in its subtree). Returns -1 if node_index is NULLPTR (representing an empty subtree). Definition at line 874 of file ZipTrie.hpp.
|
noexcept |
Inserts a key into the zip trie.
A new node (Bucket) is created for the key, assigned a random rank using RANK_T::get_random(), and added to the internal _buckets storage. The insert_recursive helper method is then called to place this new node into the correct position according to key order (search tree property) and perform zip operations based on rank comparisons to maintain the max-heap property (max-heap property). The provided key pointer is stored directly in the node; the zip trie does not take ownership.
| key | Pointer to the new key to insert. Must be non-null and point to a valid KEY_T object. |
key must exceed the lifetime of the zip trie or at least until the key is removed (if removal were implemented). The trie stores only the pointer. insert_recursive, leaving the tree structure unchanged. However, the new bucket added in this function might remain unused in the _buckets vector. Definition at line 743 of file ZipTrie.hpp.
|
protectednoexcept |
Recursive helper function for inserting a new node x.
Traverses the trie based on key comparisons (compare_func) and LCPs, updating ancestor_lcps along the path. When the correct insertion position (a NULLPTR link) is found, it inserts the node x. During the backtracking phase (as the recursion unwinds), it performs 'zip' operations to maintain the max-heap property based on ranks. If the rank of the newly inserted node x (or the root of the subtree returned by the recursive call) is higher than or equal to the rank of the current node v, the higher-ranked node is promoted. The function updates node LCPs (ancestor_lcps member) during operations to reflect the structural changes.
| CompareFunction | A callable type for comparing keys, same signature as in search_recursive. |
| x | Pointer to the new bucket being inserted (already present in _buckets). |
| x_index | Index of the new bucket x in the _buckets vector. |
| v_index | Index of the current node (v) being visited in the recursion. Starts with _root_index. |
| ancestor_lcps | LCPs inherited from the path above v_index. These are updated based on the traversal direction. |
| compare_func | The comparison function to use. |
v_index (if no update occurred at this level or insertion happened deeper) or x_index (if x was promoted up to become the new root of this subtree). compare_func determines the key already exists (returns equal), the insertion is aborted for that path, and v_index is returned, leaving the subtree rooted at v unchanged. Definition at line 766 of file ZipTrie.hpp.
|
inlineprivatenoexcept |
Compares a key x with the key in a node v, utilizing ancestor LCPs for optimization (k-compare).
This is the primary comparison function used for navigating the Zip Trie during search and insertion. It first calls k_compare_prefix_check to attempt to determine the comparison result solely based on the LCP information stored along the ancestor paths. If k_compare_prefix_check returns std::nullopt (meaning the LCP check was inconclusive), this function proceeds to perform a direct comparison of the key data (x->key vs v.key) using BitString::seq_k_compare. Crucially, the BitString comparison starts from the bit/character offset (out_start_lcp_val) determined by the prefix check, avoiding redundant comparisons of the already-matched prefix.
| x | Pointer to the key being searched for or inserted. |
| v | Constant reference to the current node (Bucket) being compared against. |
| ancestor_lcps | The LCPs computed along the path from the root down to v's parent. |
comparison) of x vs v and the calculated Longest Common Prefix (lcp) between them in the context of this comparison (converted to LCP_T format). Definition at line 649 of file ZipTrie.hpp.
|
protectednoexcept |
Performs an initial check based on ancestor LCPs before full key comparison (k-compare optimization).
This is a core optimization in zip trie navigation. It compares the LCPs accumulated along the current search path (ancestor_lcps) with the LCPs stored in the current node v (v.ancestor_lcps, which were the path LCPs when v was inserted). If these path LCPs differ significantly (x_max_lcp != corr_v_lcp), the relative order of the search key x and node v can often be determined without comparing the actual key data (x->key vs v.key). This check exploits the property that if the LCP context differs, the branching decision made when v was inserted must differ from the decision needed for x.
| v | The current node (Bucket) being considered. | |
| ancestor_lcps | The LCPs accumulated along the current search/insert path down to v's parent. | |
| [out] | out_start_lcp_val | If a full key comparison is needed (returns std::nullopt), this parameter is populated with the length (number of characters/bits) from which the actual key comparison (BitString::seq_k_compare) should start. This avoids re-comparing the prefix already known to match based on the LCP check. |
std::optional<ComparisonResult>ComparisonResult containing the determined ordering (less or greater) and the minimum of the differing LCPs (std::min(x_max_lcp, corr_v_lcp)).x_max_lcp == corr_v_lcp): returns std::nullopt. Definition at line 583 of file ZipTrie.hpp.
|
noexcept |
Finds the Longest Common Prefix (LCP) between the search key and the path taken in the trie.
If the key is found (search(key).contains == true), this returns the LCP between the search key and the found key (which is typically the key's full length, assuming k_compare calculates full LCP on equality). If the key is not found (search(key).contains == false), it returns the LCP between the search key and the path leading to the point where the search terminated (i.e., the maximum LCP computed along the search path).
| key | Pointer to the key to search for. |
MEMORY_EFFICIENT is true. Definition at line 576 of file ZipTrie.hpp.
|
virtualnoexcept |
Performs a search for a key within the ZipTrie.
Traverses the trie based on key comparisons (k_compare), updating ancestor LCPs along the path. The k_compare function utilizes stored LCP information to potentially avoid full key comparisons.
| key | Pointer to the key to search for. |
contains), the calculated maximum LCP along the path (max_lcp), and the depth if found (depth). Reimplemented in ParallelZipTrie< CHAR_T, MEMORY_EFFICIENT, RANK_T, CHAR_SIZE_BITS >.
Definition at line 671 of file ZipTrie.hpp.
|
protectednoexcept |
Recursive helper function for the public search method.
Implements the core traversal logic iteratively (using a while loop) based on the results of the provided comparison function. It tracks the depth and updates ancestor LCPs along the path.
| CompareFunction | A callable type (e.g., lambda, function pointer) that takes (const KEY_T* key, const Bucket& v_node, const AncestorLCPs& ancestor_lcps) and returns a ComparisonResult. This allows customizing the comparison logic if needed, although typically it wraps k_compare. |
| key | The key being searched for. |
| compare_func | The comparison function to use for navigating the trie. |
Definition at line 687 of file ZipTrie.hpp.
|
noexcept |
Directly adds a pre-constructed bucket to the internal storage vector.
insert), including rank generation and maintaining the zip trie properties (key order, rank heap property). Using it improperly will likely corrupt the trie structure. | key | Pointer to the key for the new bucket. |
| rank | The rank to assign to the new bucket. |
| left | Index of the left child (use NULLPTR for none). |
| right | Index of the right child (use NULLPTR for none). |
| predecessor_lcp | The ancestor LCP with the predecessor path for this bucket. |
| successor_lcp | The ancestor LCP with the successor path for this bucket. |
Definition at line 1039 of file ZipTrie.hpp.
|
noexcept |
Directly sets the root index of the trie.
root_index points to a valid, intended root node within _buckets. | root_index | The index in _buckets to set as the new root. |
Definition at line 1045 of file ZipTrie.hpp.
|
noexcept |
Returns the number of nodes (keys) currently stored in the trie.
Definition at line 857 of file ZipTrie.hpp.
|
noexcept |
Generates a DOT language representation of the trie structure for visualization.
Writes the DOT output to the specified file. This file can then be processed by tools like Graphviz (e.g., dot -Tpng input.dot -o output.png) to create a visual diagram of the trie, showing nodes (with keys, ranks, LCPs) and their parent-child relationships.
| file_path | The path (including filename) where the output DOT file should be written. |
std::cerr. Definition at line 946 of file ZipTrie.hpp.
|
privatenoexcept |
Recursive helper function to generate the DOT language representation for a subtree.
Called by the public to_dot method. It defines the current node and its edges to its children in the DOT format and then recursively calls itself for the children. Includes node index, key, rank, and stored ancestor LCPs in the node label.
| os | The output file stream (std::ofstream) to write the DOT commands to. |
| node_index | Index of the root of the subtree to output. |
Definition at line 973 of file ZipTrie.hpp.
|
protected |
A contiguous vector storing all the nodes (Buckets) of the trie.
Node relationships (parent/child) are managed via indices (_root_index, left, right) within this vector, forming the tree structure implicitly.
Definition at line 379 of file ZipTrie.hpp.
|
protected |
Stores the approximate log base 2 of the max_size provided during construction.
MEMORY_EFFICIENT is true. Definition at line 323 of file ZipTrie.hpp.
|
protected |
Stores the maximum possible LCP length provided during construction.
MEMORY_EFFICIENT is true. Definition at line 317 of file ZipTrie.hpp.
|
protected |
Index of the root node within the _buckets vector. Initialized to NULLPTR for an empty trie.
Definition at line 312 of file ZipTrie.hpp.
|
staticconstexprprotected |
Represents a null child pointer index using the maximum value of unsigned.
Definition at line 326 of file ZipTrie.hpp.