Solve RMQ (Range Minimum Query) by finding LCA (Lowest Common Ancestor)¶
Given an array A[0..N-1]
.
For each query of the form [L, R]
we want to find the minimum in the array A
starting from position L
and ending with position R
.
We will assume that the array A
doesn't change in the process, i.e. this article describes a solution to the static RMQ problem
Here is a description of an asymptotically optimal solution. It stands apart from other solutions for the RMQ problem, since it is very different from them: it reduces the RMQ problem to the LCA problem, and then uses the Farach-Colton and Bender algorithm, which reduces the LCA problem back to a specialized RMQ problem and solves that.
Algorithm¶
We construct a Cartesian tree from the array A
.
A Cartesian tree of an array A
is a binary tree with the min-heap property (the value of parent node has to be smaller or equal than the value of its children) such that the in-order traversal of the tree visits the nodes in the same order as they are in the array A
.
In other words, a Cartesian tree is a recursive data structure.
The array A
will be partitioned into 3 parts: the prefix of the array up to the minimum, the minimum, and the remaining suffix.
The root of the tree will be a node corresponding to the minimum element of the array A
, the left subtree will be the Cartesian tree of the prefix, and the right subtree will be a Cartesian tree of the suffix.
In the following image you can see one array of length 10 and the corresponding Cartesian tree.
The range minimum query [l, r]
is equivalent to the lowest common ancestor query [l', r']
, where l'
is the node corresponding to the element A[l]
and r'
the node corresponding to the element A[r]
.
Indeed the node corresponding to the smallest element in the range has to be an ancestor of all nodes in the range, therefor also from l'
and r'
.
This automatically follows from the min-heap property.
And is also has to be the lowest ancestor, because otherwise l'
and r'
would be both in the left or in the right subtree, which generates a contradiction since in such a case the minimum wouldn't even be in the range.
In the following image you can see the LCA queries for the RMQ queries [1, 3]
and [5, 9]
.
In the first query the LCA of the nodes A[1]
and A[3]
is the node corresponding to A[2]
which has the value 2, and in the second query the LCA of A[5]
and A[9]
is the node corresponding to A[8]
which has the value 3.
Such a tree can be built in $O(N)$ time and the Farach-Colton and Benders algorithm can preprocess the tree in $O(N)$ and find the LCA in $O(1)$.
Construction of a Cartesian tree¶
We will build the Cartesian tree by adding the elements one after another.
In each step we maintain a valid Cartesian tree of all the processed elements.
It is easy to see, that adding an element s[i]
can only change the nodes in the most right path - starting at the root and repeatedly taking the right child - of the tree.
The subtree of the node with the smallest, but greater or equal than s[i]
, value becomes the left subtree of s[i]
, and the tree with root s[i]
will become the new right subtree of the node with the biggest, but smaller than s[i]
value.
This can be implemented by using a stack to store the indices of the most right nodes.
vector<int> parent(n, -1);
stack<int> s;
for (int i = 0; i < n; i++) {
int last = -1;
while (!s.empty() && A[s.top()] >= A[i]) {
last = s.top();
s.pop();
}
if (!s.empty())
parent[i] = s.top();
if (last >= 0)
parent[last] = i;
s.push(i);
}