Treap
Prerequisites: Naive Binary Search Tree, Heap Basics.
Introduction
Treap (Tree + Heap) is a weakly balanced binary search tree.
Each node in a Treap has, in addition to the value (\(\textit{val}\)) being maintained, a random priority (\(\textit{priority}\)). The values satisfy the binary search tree property, while the priorities satisfy the heap property (min-heap or max-heap).
The binary search tree property means:
- The value (\(\textit{val}\)) of the left child is smaller than that of the parent node.
- The value (\(\textit{val}\)) of the right child is larger than that of the parent node.
The heap property means:
- The priority (\(\textit{priority}\)) of a child is larger or smaller than that of the parent (depending on whether it is a min-heap or max-heap).
It is not hard to see that if the same value is used, these two data structures combined would degenerate into a chain. Therefore, we introduce an additional heap value \(\textit{priority}\) on top of the search tree. For the \(\textit{val}\) values, we maintain the search tree property; for the \(\textit{priority}\) values, we maintain the heap property. The \(\textit{priority}\) value is randomly assigned.
The following figure shows an example of a Treap (here a min-heap is used, meaning the root has the smallest priority).
Why do we need to go through all this trouble to make this data structure satisfy both tree and heap properties, and assign random heap values?
To understand this, we first need to understand the problem with naive binary search trees. When inserting a new node into a naive search tree, we start recursively from the root. If the new node is smaller than the current node, we go left; otherwise, we go right.
Finally, when we find a node with no child, we make the new node its left or right child based on its value.
If the inserted node values are random (i.e., inserted in random order), the height of this naive search tree will be small (close to \(\log n\), where \(n\) is the number of nodes), and each level will have many nodes, i.e., its shape will be very "fat". The Treap in the figure above is an example. Therefore, any operation will have a complexity of about \(O(\log n)\).
However, this is only the complexity in the random case. If we insert nodes into a naive search tree in a very ordered sequence like:
1 | |
Then the tree will degenerate into a chain, becoming very "tall and thin" (each inserted node is larger than the previous ones, so they are all placed as right children):
It is easy to see that the query complexity changes from \(O(\log n)\) to \(O(n)\).
To solve this problem and achieve a more "balanced" state, Treap uses random priorities to satisfy the heap property, "shuffling" the insertion order of nodes, thus giving the binary search tree ideal complexity and avoiding the chain degeneration problem.
Proof of Treap Complexity
Since the complexity of various operations in a Treap is related to the depth of the nodes being operated on, we first prove that the expected depth of all nodes is \(O(\log n)\).
Notation Convention
For convenience, we define:
- \(n\) is the number of nodes.
- In a Treap node, the property satisfying the binary search tree is called value, and the property satisfying the heap (i.e., random) is called priority. Assume the priority satisfies the min-heap property.
- \(x_k\) denotes the node with the \(k\)-th smallest value.
- \(X_{i,j}\) denotes the set \(\{x_i,x_{i+1},\cdots,x_{j-1},x_j\}\), i.e., the set of nodes from the \(i\)-th to the \(j\)-th in ascending order of value.
- \(\operatorname{dep}(x)\) denotes the depth of node \(x\). The root depth is defined as \(0\).
- \(Y_{i,j}\) is an indicator random variable that equals \(1\) when \(x_i\) is an ancestor of \(x_j\), and \(0\) otherwise. In particular, \(Y_{i,i}=0\).
- \(\Pr(A)\) denotes the probability of event \(A\) occurring.
Proof of Expected Node Depth
Since the depth of node \(x_i\) equals the number of its ancestors, we have
Then by linearity of expectation,
Since \(Y_{k,i}\) is an indicator random variable, its expectation equals the probability that it is \(1\), so
We first prove a lemma: \(Y_{i,j}=1\) if and only if \(x_i\) has the smallest priority in \(X_{i,j}\).
Proof of Lemma
Consider cases for \(x_i\) and \(x_j\).
- If \(x_i\) is the root: Since priorities satisfy the min-heap property, \(x_i\) has the smallest priority, and for any \(x_j\), \(x_i\) is an ancestor of \(x_j\).
- If \(x_j\) is the root: Similarly, \(x_j\) has the smallest priority, so \(x_i\) is not the smallest in \(X_{i,j}\); also \(x_i\) is not an ancestor of \(x_j\).
- If \(x_i\) and \(x_j\) are in two different subtrees of the root (one left, one right), then the root \(r\in X_{i,j}\). Therefore, \(x_i\) cannot have the smallest priority in \(X_{i,j}\) (because the root's priority is smaller). Also, since \(x_i\) and \(x_j\) belong to different subtrees, \(x_i\) is not an ancestor of \(x_j\).
- If \(x_i\) and \(x_j\) are in the same subtree of the root, we can treat this subtree as a new Treap and recursively apply the above proof.
Then according to the lemma, the expected depth can be transformed into
Since node priorities are random, we assume that any node in set \(X_{i,j}\) has equal probability of being the smallest. Then
Therefore, the expected depth of each node is \(O(\log n)\).
The operations of a naive binary search tree all have complexity \(O(h)\), and maintaining the heap property in a Treap also has complexity \(O(h)\). Thus, the expected complexity of all Treap operations is \(O(\log n)\).
Intuitive Understanding of Expected Complexity
First, we need to recognize that a node's \(\textit{priority}\) property is directly related to its level in the tree. Recall the heap property:
- Child node value (\(\textit{priority}\)) is larger or smaller than the parent (depending on min-heap or max-heap).
We find that nodes at lower levels, such as the root of the entire tree, have smaller \(\textit{priority}\) (in a min-heap). Moreover, in a naive search tree, nodes inserted earlier are more likely to have lower levels. We can associate this \(\textit{priority}\) property with insertion order to understand why Treap can shuffle node insertion order via \(\textit{priority}\).
When inserting a new node into a Treap, we need to maintain both tree and heap properties. The search tree property can be maintained during insertion, while the heap property can be maintained in two ways: rotation, and split/merge. Treaps using these two methods are called rotating Treap and non-rotating Treap, respectively.
Rotating Treap
Rotating Treap maintains balance through rotation, similar to AVL tree rotations, divided into left rotation and right rotation. That is, under the condition of satisfying the binary search tree, balance operations are performed on the Treap based on heap priorities.
Rotating Treap has relatively small constant factors among all balanced trees when solving ordinary balanced tree problems.
The code explanation below implements a rotating Treap using pointers; a full array-based implementation is provided at the end.
Info
In the code, rank represents the priority (\(\textit{priority}\) property) mentioned earlier, which satisfies the min-heap property.
Node Structure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Rotation
Rotation is a very important operation in Treap, mainly used to adjust the levels of different nodes while preserving the Treap tree properties, in order to maintain the heap property.
Left and right rotations might not be easy to distinguish; here are two more obvious characteristics:
Meaning of rotation:
- Without affecting the search tree property, turn the subtree opposite the rotation direction into the root node (e.g., left rotation turns the right subtree into the root)
- Does not affect properties, and after rotation, the child node in the same direction as the rotation becomes the original root (e.g., after left rotation, the left child becomes the original root)
Left and right rotations are inverse operations, as shown in the figure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Insertion
Similar to insertion in an ordinary binary search tree, but rotation is needed during insertion to maintain the heap property of priorities.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Deletion
Mainly involves case analysis; different cases have different handling methods. After deletion, the tree size changes, so update is needed. If the node to be deleted has both left and right subtrees, consider who should become the parent after deletion (maintain the node with smaller rank on top).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
Query Rank by Value
Operation meaning: Query the rank (number of nodes smaller than val in the subtree + 1) of value val in the subtree rooted at cur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Query Value by Rank
To query a value by rank, we first need to know how to determine which part of the tree the node is in:
The following table shows a judgment method:
| Left Subtree | Root Node / Current Node | Right Subtree |
|---|---|---|
| rank ≤ left subtree size | rank > left subtree size and ≤ left subtree size + root repeat count | rank > left subtree size + root repeat count |
Note that if in the right subtree, the original rank needs to be adjusted during recursion. Recursion then queries the value at that rank in the right subtree. To convert the rank to be based on the right subtree, subtract the left subtree size and the root's repeat count.
Think of all nodes as a sorted array or number line (as below),
1 2 3 4 5 6 7 | |
The conversion method is simply to subtract the left subtree size and the root's repeat count from the rank.
1 2 3 4 5 6 7 8 9 10 11 | |
Query First Node Smaller Than val
Note that a global variable q_prev_tmp in the class is used here.
This value is only updated when val is larger than the current node's value, so returning this variable returns the last time val was larger than the current node's value, after which it becomes smaller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Query First Node Larger Than val
Similar to the previous one, just swap the inequality signs.
1 2 3 4 5 6 7 8 9 10 | |
Non-rotating Treap
The operational approach of non-rotating Treap makes it inherently support sequence maintenance, persistence, and other features.
Non-rotating Treap is also called split-merge Treap. It has only two core operations: split and merge. Through these two operations, many other operations can be implemented more conveniently than with rotating Treap. These two operations are introduced below.
Note
Discussion of non-rotating Treap should mention FHQ-Treap (by Fan Haoqiang). That is, a persistent, interval-operation supporting non-rotating Treap. For more content, refer to the ppt "Fan Haoqiang on Data Structures".
Split
Split by Value
The split operation takes two parameters: root pointer \(\textit{cur}\) and key value \(\textit{key}\). The result splits the Treap pointed to by the root pointer into two Treaps: the first Treap contains all nodes with value (\(\textit{val}\)) less than or equal to \(\textit{key}\), and the second Treap contains all nodes with value greater than \(\textit{key}\).
The process first checks if \(\textit{key}\) is less than \(\textit{cur}\)'s value. If so, then \(\textit{cur}\) and its entire right subtree are greater than \(\textit{key}\), belonging to the second Treap. However, part of the left subtree may also be greater than \(\textit{key}\), so we need to continue recursively splitting the left subtree. For the part of the left subtree greater than \(\textit{key}\), we attach it as \(\textit{cur}\)'s left subtree, so that all nodes on \(\textit{cur}\) are greater than \(\textit{key}\).
Correspondingly, if \(\textit{key}\) is greater than or equal to \(\textit{cur}\)'s value, then \(\textit{cur}\)'s entire left subtree and itself are less than or equal to \(\textit{key}\), belonging to the first Treap after split. Also, part of \(\textit{cur}\)'s right subtree may be less than or equal to \(\textit{key}\), so we need to recursively split the right subtree. Attach the part less than or equal to \(\textit{key}\) as \(\textit{cur}\)'s right subtree, so that all nodes on \(\textit{cur}\) are less than or equal to \(\textit{key}\).
The figure below shows split by value when \(\textit{cur}\)'s value is less than or equal to \(\textit{key}\).1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Split by Rank
Compared to split by value, this operation is more like querying value by rank in rotating Treap (a node's rank is the number of nodes with smaller values in the tree \(+ 1\)):
This function takes two parameters: node pointer \(\textit{cur}\) and rank \(\textit{rk}\), and returns three Treaps after split.
The first Treap contains nodes with rank less than \(\textit{rk}\), the second has rank equal to \(\textit{rk}\) (and only one node, since multiple equal ones would increase the cnt field in the Node struct), and the third has rank greater.
The key point of this operation is determining which part of the tree contains the node with rank equal to \(\textit{cur}\). This is also an important part of querying value by rank in rotating Treap, explained in detail earlier, so not repeated here.
Also, the recursive part of this operation is very similar to split by value, not elaborated here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
Merge
The merge operation takes two parameters: left Treap root pointer \(\textit{u}\) and right Treap root pointer \(\textit{v}\). It must satisfy that all node values in \(\textit{u}\) are less than or equal to all node values in \(\textit{v}\). Generally, the two Treaps we merge are originally split from one Treap, so it's easy to satisfy that all nodes in \(\textit{u}\) are smaller than those in \(\textit{v}\).
In rotating Treap, we use rotation to maintain \(\textit{priority}\) satisfying heap properties while not changing tree properties. In non-rotating Treap, we use merge to achieve the same effect.
Since the two Treaps are already ordered, during merge we only need to decide which tree to "place on top" and which "below", i.e., which tree to use as a subtree. Clearly, according to heap properties, we need to place the one with smaller \(\textit{priority}\) on top (here using min-heap).
Also, we need to satisfy the search tree property, so if the root node of \(\textit{u}\) has smaller \(\textit{priority}\) than \(\textit{v}\)'s, then \(\textit{u}\) becomes the new root, and \(\textit{v}\), having larger values than \(\textit{u}\), should merge with \(\textit{u}\)'s right subtree; otherwise, \(\textit{v}\) becomes the new root, and since \(u\)'s values are smaller than \(\textit{v}\)'s, merge with \(v\)'s left subtree.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Insertion
In non-rotating Treap, basic operations like insertion, deletion, and querying rank by value can be implemented either using ordinary binary search tree methods or using split and merge. Generally, using split and merge is more concise but slightly slower2. To help better understand non-rotating Treap, the following operations all use split and merge.
When implementing insertion, we utilize some properties of split. That is, nodes with value less than or equal to \(\textit{val}\) will be split into the first Treap.
So, suppose we split the current Treap according to \(\textit{val}\). We get two trees satisfying:
where \(T_1\) denotes the set of nodes assigned to the first Treap after split, and \(T_2\) the second.
If we further split \(T_1\) by \(\textit{val} - 1\), we get two trees satisfying:
where \(T_{1\ \text{left}}\) denotes the set of nodes assigned to the first Treap after splitting \(T_1\), and \(T_{1\ \text{right}}\) the second. The condition \(\And \ T_{1\ \text{right}} \le val\) in the latter half comes from \(T_1\)'s condition \(T_1 \le val\).
It's not hard to see that as long as \(\textit{val}\) and node values are integers (as in most use cases), there is exactly one node satisfying \(T_{1\ \text{right}}\), i.e., the node with value equal to \(\textit{val}\).
During insertion, if we find a node satisfying \(T_{1\ \text{right}}\) exists, we can simply increase its repeat count; otherwise, create a new node.
Note that after splitting, we need to merge the trees back together using merge so they can be used again later. Also, note that the order of parameters in merge matters: all nodes in the first tree must be smaller than those in the second.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Deletion
Deletion also uses a method similar to insertion: find the node with value equal to \(\textit{val}\) and delete it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Query Rank by Value
Rank is the number of nodes smaller than this value \(+ 1\), so we split the current tree by \(\textit{val} - 1\). Then the first tree after split satisfies:
If tree values and \(\textit{val}\) are integers, then \(T_1\) contains all nodes with value less than \(\textit{val}\).
1 2 3 4 5 6 | |
Query Value by Rank
Calling split_by_rk() returns three split Treaps, where the second contains only one node with rank equal to \(\textit{rk}\), so we directly return this node's \(\textit{val}\).
1 2 3 4 5 6 7 | |
Query First Node Smaller Than val
This problem can be transformed into: among all nodes smaller than \(\textit{val}\), find the one with the largest rank. We split the Treap by \(\textit{val}\); the first Treap returned contains all nodes with values less than \(\textit{val}\). Then we call qval_by_rank() to find the node with the largest value in this tree.
1 2 3 4 5 6 7 8 | |
Query First Node Larger Than val
Similar to the previous operation, this can be transformed into: among all nodes larger than \(\textit{val}\), find the one with the smallest rank. After splitting by \(\textit{val}\), the second Treap returned contains all nodes with values greater than \(\textit{val}\).
Then we query the value of the node with rank \(1\) in this tree (i.e., the node with the smallest value), successfully obtaining the first node larger than \(\textit{val}\).
1 2 3 4 5 6 7 | |
Building a Tree (build)
Convert a sequence \(\{a_n\}\) of \(n\) nodes into a Treap.
We can insert these \(n\) nodes sequentially. Each time inserting a node with value \(v\), split the entire Treap into two parts: nodes with value \(\le v\) and nodes with value \(> v\). Then create a new node with value \(v\), and merge the two parts and the new node in increasing order. Single insertion time \(O(\log n)\), total time \(O(n\log n)\).
In some problems, there may be multiple insertions of ordered sequences, requiring building a tree in \(O(n)\) time.
Method 1: During recursive tree building, each time pick the midpoint of the current interval as the tree root for that interval, and assign appropriate priorities to each node so that the new tree satisfies heap properties. This ensures tree height \(O(\log n)\).
Method 2: During recursive tree building, each time pick the midpoint of the current interval as the tree root, then assign random priorities to each node. This ensures tree height \(O(\log n)\) but does not guarantee heap properties. This is still correct because priorities in non-rotating Treap are used to make merge more random, not to guarantee tree height.
Method 3: Observe that Treap is a Cartesian tree; use the \(O(n)\) Cartesian tree building method with a monotonic stack to maintain the right chain.
Interval Operations in Non-rotating Treap
Building a Tree
One major advantage of non-rotating Treap over rotating Treap is the ability to perform various interval operations. Below we use the template problem of Literary Balanced Tree as an example to introduce interval operations in Treap.
You need to write a data structure (refer to the problem title) to maintain an ordered sequence.
Required operations: flip an interval. For example, original sequence \(5\ 4\ 3\ 2\ 1\), flipping interval \([2,4]\) yields \(5\ 2\ 3\ 4\ 1\). For \(100\%\) data, \(1 \le n\) (initial interval length) \(m\) (number of flips) \(\le 10^5\)
In this problem, we need to implement interval flipping. First consider how to build the tree; the built tree should represent the initial interval.
We simply insert the indices of the interval sequentially into the Treap. Then an in-order traversal (left subtree, then current node, then right subtree) yields this interval3.
We know that inserting nodes in increasing order into a naive binary search tree results in a long chain; in-order traversal naturally gives the interval.
As shown above, inserting nodes in order \(1\ 2\ 3\ 4\ 5\) into a naive search tree yields \(1\ 2\ 3\ 4\ 5\) in in-order traversal.
But in a Treap, after inserting nodes in increasing order, the merge operation adjusts the tree structure according to \(\textit{priority}\). In this case, how can we ensure in-order traversal outputs correctly?
Refer to the monotonic stack building method for Cartesian trees to understand this.
Let the newly inserted node be \(\textit{u}\).
First, because nodes are inserted in increasing order, each new node will be connected to the right chain of the Treap (the chain from the root always going to the right child).
Starting from the root, the \(\textit{priority}\) of nodes on the right chain is increasing (min-heap). We can find the first node on the right chain with \(\textit{priority}\) greater than \(\textit{u}\), call this node \(\textit{v}\), and replace it with \(\textit{u}\).
Since \(\textit{u}\) must be greater than all other nodes in the tree, we need to attach \(\textit{v}\) and its subtree as \(\textit{u}\)'s left subtree. And \(\textit{u}\) has no right subtree at this point.
We can see that in in-order traversal, \(\textit{u}\) will definitely be the last visited (because \(\textit{u}\) is the last in the right chain, and in in-order traversal, the right subtree is visited last).
The figure below shows the changes when inserting node \(5\) into a Treap after inserting nodes \(1 \sim 5\) in increasing order. This figure helps understand the process of insertion in increasing order.
Interval Flip
When flipping interval \([l, r]\), the basic idea is to split the tree into three intervals: \([1, l - 1],\ [l, r],\ [r + 1, n]\), then flip the middle \([l, r]\)3.
The specific flip operation swaps the left and right children of every node in the interval. The figure below shows the Treap after flipping intervals \([3, 4]\) and \([3, 5]\) in the Treap from the previous figure.
Note that if we flip this way, each time we flip interval \([l, r]\), \(r - l\) nodes would have their children swapped. Such frequent operations obviously cannot handle \(10^5\) data range; its \(O(n \times \log_2 n)\) flip complexity per operation is even worse than brute force (because besides linear time swapping nodes, we also need \(O(\log_2 n)\) time to find the nodes to swap).
Observing the problem requirements, since we only need to output the final interval after operations, we don't actually need to swap every time. Thus, we can use lazy tags (lazy propagation), commonly used in segment trees, to optimize complexity. When flipping, we only need to mark the parent node, indicating that every left/right child in the subtree below needs to be swapped.
In segment trees, we usually propagate lazy tags during updates and queries. This is because the range we want to update/query may not coincide with the range represented by the lazy tag, so we propagate first to ensure correct values.
The same applies in non-rotating Treap. Specifically, we split the Treap into the three trees mentioned earlier, mark the middle tree with a lazy tag, then merge the three trees. Because the interval we want to flip may not coincide with the lazy tag's range, we need to propagate the tag during splitting. Also, split and merge operations cause each node and its lazy tag's represented nodes to change, so we also need to propagate lazy tags before merging.
In other words, when the tree structure changes—when we perform split or merge operations and need to change a node's left/right child information—we should propagate the tag beforehand, not after, because lazy tags need to be passed to child nodes. If we change child information before propagating, the lazy tag loses its target.4
The following explains the code, referencing3.
Since most operations in interval operations are the same as ordinary non-rotating Treap, only the differences are explained here.
Propagating Tags
Note that the lazy tag here means we need to swap every child node in this tree. So if the current node's child also has a lazy tag, the two flips cancel. If the child does not need flipping, this lazy tag needs to be propagated further to the child.
1 2 3 4 5 6 7 8 9 10 11 | |
Split
Note that in this problem, due to flip operations, the \(\textit{val}\) in the Treap no longer satisfies the binary search tree property (see interval flip figure), so we cannot decide whether to recurse left or right based on \(\textit{val}\).
Thus, this split is more similar to split by rank in ordinary non-rotating Treap; it decides recursion direction based on the current tree size, i.e., based on the node's original position in the tree.
The first Treap returned contains nodes with rank all \(\le \textit{sz}\), while the second contains nodes with rank all \(> \textit{sz}\).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Merge
The only note is to propagate lazy tags before merging.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Interval Flip
As described earlier, split into \([1, l - 1],\ [l, r],\ [r + 1, n]\) three intervals, mark the middle interval, then merge.
1 2 3 4 5 6 7 8 9 | |
In-order Traversal Print
Remember to propagate tags when printing.
1 2 3 4 5 6 7 8 | |
Complete Code
Rotating Treap
Pointer Implementation
Complete Code
The following is the complete version of the code explained earlier, a template for ordinary balanced tree problems.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
Array Implementation
Below is the template code for ordinary balanced tree on bzoj, implemented with arrays.
Complete Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
Non-rotating Treap
Pointer Implementation
Complete Code
The following is the complete version of the code explained earlier, a template for ordinary balanced tree problems.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
Interval Operations in Non-rotating Treap
Pointer Implementation
Complete Code
The following is the complete version of the code explained earlier, a template for the Literary Balanced Tree problem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
Example Problems
Literary Balanced Tree (Splay)
References and Notes
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用