Implicit Treap (Treap Indexed by Implicit Key)

Lecture



Treap with Implicit Key

Basic idea

Consider the dynamic array data structure. In its standard implementation we can append an element to the end of the vector, find out the value of the element at a given position, change an element by its index, and remove the last element. Suppose we need a data structure with the properties above, plus the following operations: insert an element at any position (with the corresponding renumbering of the elements) and remove any element (also with the corresponding renumbering). Such a structure can be implemented on the basis of a treap, and the result is often called a treap with an implicit key.

Key X

As is known, a treap is a data structure combining a binary search tree and a binary heap. To implement a treap with an implicit key, we modify this structure. Namely, we keep in it only the priority Y, and instead of the key X we use the following quantity: the number of elements in our structure that lie to the left of our element. In other words, we take the key to be the ordinal position of our element in the tree, decreased by one.

Note that this preserves the structure of a binary search tree with respect to this key (that is, the modified treap remains a treap). However, this approach introduces a problem: the operations of adding and removing an element can change the numbering, and with a naive implementation, updating all the keys would take O(n) time, where n is the number of elements in the tree.

Auxiliary quantity C

This problem is solved quite simply. The main idea is that this key X is not itself stored anywhere. Instead, we store an auxiliary quantity C: the number of vertices in the subtree of our vertex (the vertex itself is included in the subtree). Note that all operations on an ordinary treap were performed from the top down. Also note that if, along the path from the root to some vertex, we sum up all such quantities for the left subtrees we did not go into, each increased by one, then upon reaching the vertex itself and adding to this sum the number of elements in its left subtree, we get exactly its key X.

Implicit Treap (Treap Indexed by Implicit Key)

Operations maintaining the treap structure

The structure of an ordinary treap is maintained using two operations: split — splitting one treap into two such that in one the key X is smaller than a given value, and in the other it is larger, and merge — merging two trees, in one of which all keys X are smaller than in the second. Taking into account the differences between a treap with an implicit key and an ordinary one, the operations are now described as follows: split(root,t) — splitting the tree into two so that the left one ends up with exactly t vertices, and merge(root1,root) — merging any two trees, respectively.

Split

Suppose the split procedure is launched at the root of the tree with the requirement to cut off k vertices from the tree. It is also known that the left subtree of the vertex has l vertices, and the right one has r. Consider all possible cases:

  • l⩾k. In this case we need to recursively run the split procedure from the left child with the same parameter k. Here the new left child of the root will become the right part of the recursive procedure's answer, and the right part of the answer will become the root.
  • <k This case is symmetric to the previous one. We recursively run the split procedure from the right child with parameter k−l−1. Here the new right child of the root will become the left part of the recursive procedure's answer, and the left part of the answer will become the root.

Pseudocode:

 ⟨⟨Treap, Treap ⟩ split(Treap t, int k)
  if t == ∅
    return  ⟨ ∅,  ∅ ⟩
  int l = t.left.size
  if l ⩾  k
     ⟨t1, t2⟩  = split(t.left, k)
    t.left = t2
    update(t)
    return ⟨t1, t ⟩
  else
     ⟨t1, t2⟩  = split(t.right, k - l - 1)
    t.right = t1
    update(t)
    return  ⟨t, t2⟩

Merge

Let us look at any implementation of the merge procedure. Note that it never accesses the key X. Therefore the implementation of the merge procedure for a treap with an implicit key will not differ at all from the implementation of the same procedure for an ordinary treap.

Maintaining the correctness of the C values

The only action needed to ensure the correctness of these values is that after any operation on a vertex's children, its field C must be set to the sum of these values in its new children, increased by one.

Pseudocode:

void update(Treap t)
  t.size = 1 + t.left.size + t.right.size

Applications of the treap with an implicit key

Thus, we have described a structure from which we can cut off a part of arbitrary length on the left and merge any two parts into one in the required order. We are now able to:

  • insert an element at any position (cut off the required number of elements on the left, merge the left tree with a tree made of the one added element, and merge the result with the right tree),
  • move any piece of the array anywhere (make the required cuts and merges in the correct order),
  • perform group operations on elements. Recall the implementation of such operations in a segment tree, and it becomes clear that nothing prevents us from doing the same with the structure described here. Group operations naturally include computing a function over a segment,
  • by building two trees from the elements of different parity on a single source array, one can solve the problem of swapping even and odd elements on a segment,
  • using the ideas of the treap with an implicit key, one can implement a data structure such as a Rope.

See also

  • Splay tree
  • Treap
  • Farach-Colton and Bender algorithm
  • Reducing the LCA problem to the RMQ problem

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Structures and data processing algorithms."

Terms: Structures and data processing algorithms.