Problem
Definition (Range Min Query). Let . Given , , return .
Basic Solutions
A data structure for the Range Min Query (RMQ) has complexity if it has pre-processing and query-answering time and on input instance of size , respectively.
Precompute-None
Scan to find the min.
- This is an solution.
Complete Binary Tree
Construct a complete binary tree with being the leaf nodes. Each internal node of the tree is augmented with the minimum value of its subtree.
- This is an solution.
Precompute-All
Create a table . For each , , the -th entry records .
- This is an solution.
-
.
- Dynamic Programming.
- ,
-
.
Sparse Table
Create a table . For each , , the -th entry records .
- This is an solution.
-
.
- Dynamic Programming.
- For each , , let
-
.
Divide and Conquer
Preprocessing
-
Pick , and let .
-
Partition into blocks , such that contains the first elements, contains the next elements, and so on. In general,
- Denote be the minimum element in , respectively.
Query
-
Given , , consider
-
be the collection of indexes of the blocks :
If , let and .
-
be the collection of indexes of the blocks such that and
It must be that .
-
-
To return , return the minimum of
- , if .
- .
Note that
- The first one is a range-min query on .
- The second one consists of range-min queries on the blocks for
Approach One
-
Suppose we invoke an algorithm to answer the range-min query on , with complexity .
-
Suppose we invoke an algorithm to answer the range-min query range-min queries on the blocks for , with complexity .
- Since the set depends on the range min query, to answer arbitrary range min query, we need to run the pre-processing of on each block .
This approach has complexity
Instantiation
Two-Level Precompute-None
Choose Precompute-None for both and .
- Total preprocessing time: .
- Total query time: , if .
Hence, it has complexity .
Two-Level Binary Tree
This is just a complete binary tree over . Therefore,
-
Total preprocessing time: .
-
Total query time: .
Hence, it has complexity .
Two-Level Sparse Table
We don't investigate Precompute-All, as the sparse table is always better.
Choose sparse table for both and .
-
Total preprocessing time: .
Since , we havewhich is minimized when
Therefore, the pre-processing time is reduced to
-
Total query time: .
Hence, it has complexity .
Remark: Personally I suspect this approach is fast in practice. Since involves a linear scan of at most two blocks with size , and linear scan is cache-friendly.
Remark: If we apply two level , then the preprocessing time is
Choosing minimized this to
Further, the query time remains .
Hybrid-Sparse Table and Precompute-None
Choose sparse table for and Precompute-None for .
-
Total preprocessing time: .
-
Total query time: .
If we choose , then
-
Total preprocessing time: .
-
Total query time: .
Hence, it has complexity .
Hybrid-Sparse Table and Binary Tree
Choose sparse table for and complete binary tree for .
-
Total preprocessing time: .
-
Total query time: .
If we choose , then
-
Total preprocessing time: .
-
Total query time: .
Hence, it has complexity .
Remark: If we apply two level , then the preprocessing time is
Further, the query time is
Unfortunately, this is still .
Approach Two
Can we achieve under approach one? Recall that approach one has complexity
Suppose we want to achieve preprocessing time and query time , then and needs to satisfy that and . But this is exactly the property we want to achieve!
It seems to be impossible to achieve the desired complexity under the divide and conquer framework.
The key is to observe that the following are two different problems.
- Finding a RMQ algorithm with complexity .
- Finding an algorithm that preprocess the all block in time, such that the range-min query on each can be answered in time.
For the divide-and-conquer framework to work to achieve complexity , it suffices to find an algorithm for the second problem.
Theorem. There exists some , such that we can find an algorithm that preprocess the all block in time, such that the range-min query on each can be answered in time.
Denote such an algorithm as . Since , choosing sparse table algorithm to answer the range-min query on incurs additional preprocessing time . Further, answering a query requires only time.
It is left to show the construction of .
Block Type
W.L.O.G., consider the first block be a block of elements. We can construct a binary tree over the elements, such that it satisfies
- Min Heap Property: each node of the tree is the smallest one in the subtree rooted at this node.
- The in-order traversal of the tree gives the array .
Such a binary tree is called a Cartesian Tree and the type of the block.
On the other hand, suppose we are given a Cartesian Tree tree constructed on , and suppose that the value of each node of the tree is hidden. Hence, we only know the topology or shape of the binary tree. For all , we can still return the index of the minimum element among , purely based on the topology of the binary tree.
In this sense, the answers to the RMQ on is entirely determined by its corresponding Cartesian Tree.
Enumeration
This motivates the following strategy.
-
Enumerate all possible Cartesian Trees.
-
For each Cartesian Tree, invoke the sparse table algorithm algorithm on this tree, so that all possible range min query for this tree can be answer in time.
-
For each block , determine its Cartesian Trees. To answer a RMQ on , access the pre-computed result on its corresponding Cartesian Trees.
Fact. The number of distinct Cartesian Trees with size is given by
Therefore, enumerating the Cartesian Trees, and constructing sparse tables on them for RMQ, take time and space
Choosing gives .
Determining the Block Type
In this section, we show how to construct the corresponding Cartesian Tree given the block , and to create a unique identifier for the Cartesian Tree.
We illustrate the idea with an example. In this example, . We will construct the Cartesian Tree, by including the elements from one by one. Further, we maintain a stack and a stack operation sequence, both of which are initially empty. The stack keeps elements from the right spine of the tree, and the stack operation sequence records the "in stack" and "out stack" operations as we include more elements from into the tree.
We first include the element into the tree. Since the tree consists of only one node, it is the root of the tree. It is also the only node in the right spine of the tree, so we put into the stack, and append to the stack operation sequence, to record the in-stack operation.
Next we include the element into the tree. Since the tree satisfies min-heap property, must be the child of . Since the in-order traversal of the tree gives the array , the node must be the right child of . We put into the stack, and append to the stack operation sequence, to record the in-stack operation.
We want to include the element into the existing tree. Since after inclusion, the in-order traversal of the tree gives the array , the node must be in the right spine of the tree. Indeed, this is a general principle for adding a new element into the tree.
Lemma (In the Right Spine). For each , let be the Cartesian tree constructed on . Then be must in the right spine of .
Proof.
- If has right-child in , then it is not the last element visited by the in-order traversal of .
- If has a parent node in and is the left-child of its parent, then it is not the last element visited by the in-order traversal of .
In this case, the right spine consists of and . We compare with . As it is smaller than , we pop out of the stack. We compare with . As it is larger than , we put into the stack. We include as the right-child of , and set as the left child of . We see that after these operations, the stack still contains the nodes in the right spine of the tree. Further, the min-heap property of the tree is maintained. We append to the stack operation sequence, to record the out-stack and in-stack operations performed.
We now formally states the rules for adding an element to the right spine.
Incremental Construction Rule. For each , let be the Cartesian tree constructed on , and suppose that are the elements in the right spine of . Then is added to as follows:
If , then set as the left child of .
If , then set as the right child of .
Otherwise, there exists , such that . Replace by as the right child of , and set as the left child of .
It is easy to verify that after the construction
- is in the right spine of the tree.
- The min-heap property is maintained.
We want to include the element into the existing tree. Since after inclusion, the in-order traversal of the tree gives the array , the node must be in the right spine of the tree. In this case, the right spine consists of and . As is smaller than , we pop and out of the stack, put into the stack, and set as the left child of . We append to the stack operation sequence, to record the stack operations performed.
Finally, we pop out of the stack, and append to the stack operation sequence, to record the stack operation performed. We perform this operation, so that whatever sequence of , the stack operation sequence corresponding to the Cartesian Tree construction procedure stated as above has length .
The construction also reveal an interesting fact: each Cartesian Tree with node corresponding a - sequence with length , such that each prefix of the sequence contains no less 's than 's.
We know that
which gives a construct proof of the number of binary trees with nodes.
Reference
[1] Stanford, CS 166, Range Minimum Queries