Corrections
This commit is contained in:
parent
aec16a3445
commit
32a575404e
147
luku18.tex
147
luku18.tex
|
@ -3,23 +3,25 @@
|
||||||
\index{tree query}
|
\index{tree query}
|
||||||
|
|
||||||
This chapter discusses techniques for
|
This chapter discusses techniques for
|
||||||
efficiently performing queries for a rooted tree.
|
efficiently processing queries related
|
||||||
The queries are related to subtrees and paths
|
to subtrees and paths of a rooted tree.
|
||||||
in the tree.
|
|
||||||
For example, possible queries are:
|
For example, possible queries are:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item what is the $k$th ancestor of node $x$?
|
\item what is the $k$th ancestor of a node?
|
||||||
\item what is the sum of values in the subtree of node $x$?
|
\item what is the sum of values in the subtree of a node?
|
||||||
\item what is the sum of values in a path between nodes $a$ and $b$?
|
\item what is the sum of values in a path between two nodes?
|
||||||
\item what is the lowest common ancestor of nodes $a$ and $b$?
|
\item what is the lowest common ancestor of two nodes?
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
\section{Finding ancestors}
|
\section{Finding ancestors}
|
||||||
|
|
||||||
The $k$th ancestor of node $x$ in the tree is found
|
\index{ancestor}
|
||||||
when we ascend $k$ steps in the tree beginning at node $x$.
|
|
||||||
Let $f(x,k)$ denote the $k$th ancestor of node $x$.
|
The $k$th \key{ancestor} of a node $x$ in a rooted tree
|
||||||
|
is the node that we will reach if we move $k$
|
||||||
|
levels up from $x$.
|
||||||
|
Let $f(x,k)$ denote the $k$th ancestor of $x$.
|
||||||
For example, in the following tree, $f(2,1)=1$ and $f(8,2)=4$.
|
For example, in the following tree, $f(2,1)=1$ and $f(8,2)=4$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -45,15 +47,16 @@ For example, in the following tree, $f(2,1)=1$ and $f(8,2)=4$.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
A straighforward way to calculate $f(x,k)$
|
An easy way to calculate the value of $f(x,k)$
|
||||||
is to move $k$ steps upwards in the tree
|
is to perform a sequence of $k$ moves in the tree.
|
||||||
beginning from node $x$.
|
|
||||||
However, the time complexity of this method
|
However, the time complexity of this method
|
||||||
is $O(n)$ because it is possible that the tree
|
is $O(n)$, because the tree may contain
|
||||||
contains a chain of $O(n)$ nodes.
|
a chain of $O(n)$ nodes.
|
||||||
|
|
||||||
As in Chapter 16.3, any value of $f(x,k)$
|
Fortunately, it turns out that
|
||||||
can be efficiently calculated in $O(\log k)$
|
using a technique similar to that
|
||||||
|
used in Chapter 16.3, any value of $f(x,k)$
|
||||||
|
can be efficiently calculated in $O(\log k)$ time
|
||||||
after preprocessing.
|
after preprocessing.
|
||||||
The idea is to precalculate all values $f(x,k)$
|
The idea is to precalculate all values $f(x,k)$
|
||||||
where $k$ is a power of two.
|
where $k$ is a power of two.
|
||||||
|
@ -72,12 +75,12 @@ $\cdots$ \\
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
The value $0$ means that the $k$th ancestor
|
The value $0$ means that the $k$th ancestor
|
||||||
of a node doesn't exist.
|
of a node does not exist.
|
||||||
|
|
||||||
The preprocessing takes $O(n \log n)$ time
|
The preprocessing takes $O(n \log n)$ time,
|
||||||
because each node can have at most $n$ ancestors.
|
because each node can have at most $n$ ancestors.
|
||||||
After this, any value $f(x,k)$ can be calculated
|
After this, any value of $f(x,k)$ can be calculated
|
||||||
in $O(\log k)$ time by representing the value $k$
|
in $O(\log k)$ time by representing $k$
|
||||||
as a sum where each term is a power of two.
|
as a sum where each term is a power of two.
|
||||||
|
|
||||||
\section{Subtrees and paths}
|
\section{Subtrees and paths}
|
||||||
|
@ -215,18 +218,18 @@ nodes in the subtree of node $4$:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Using this fact, we can efficiently process queries
|
Using this fact, we can efficiently process queries
|
||||||
that are related to subtrees of the tree.
|
that are related to subtrees of a tree.
|
||||||
As an example, consider a problem where each node
|
As an example, consider a problem where each node
|
||||||
is assigned a value, and our task is to support
|
is assigned a value, and our task is to support
|
||||||
the following queries:
|
the following queries:
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item change the value of node $x$
|
\item update the value of a node
|
||||||
\item calculate the sum of values in the subtree of node $x$
|
\item calculate the sum of values in the subtree of a node
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Let us consider the following tree where blue numbers
|
Consider the following tree where the blue numbers
|
||||||
are values of nodes.
|
are the values of the nodes.
|
||||||
For example, the sum of values in the subtree of node $4$
|
For example, the sum of the subtree of node $4$
|
||||||
is $3+4+3+1=11$.
|
is $3+4+3+1=11$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -263,8 +266,8 @@ is $3+4+3+1=11$.
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
The idea is to construct a node array that contains
|
The idea is to construct a node array that contains
|
||||||
three values for each node: (1) identifier of the node,
|
three values for each node: (1) the identifier of the node,
|
||||||
(2) size of the subtree, and (3) value of the node.
|
(2) the size of the subtree, and (3) the value of the node.
|
||||||
For example, the array for the above tree is as follows:
|
For example, the array for the above tree is as follows:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -314,8 +317,8 @@ For example, the array for the above tree is as follows:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Using this array, we can calculate the sum of nodes
|
Using this array, we can calculate the sum of values
|
||||||
in a subtree by first reading the size of the subtree
|
in any subtree by first finding out the size of the subtree
|
||||||
and then the values of the corresponding nodes.
|
and then the values of the corresponding nodes.
|
||||||
For example, the values in the subtree of node $4$
|
For example, the values in the subtree of node $4$
|
||||||
can be found as follows:
|
can be found as follows:
|
||||||
|
@ -370,22 +373,24 @@ can be found as follows:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
The remaining step is to store the values of the
|
To support the queries efficiently,
|
||||||
|
it suffices to store the values of the
|
||||||
nodes in a binary indexed tree or segment tree.
|
nodes in a binary indexed tree or segment tree.
|
||||||
After this, we can both calculate the sum
|
After this, we can both update a value
|
||||||
of values and change a value in $O(\log n)$ time,
|
and calculate the sum of values in $O(\log n)$ time.
|
||||||
so we can efficiently process the queries.
|
|
||||||
|
|
||||||
\subsubsection{Path queries}
|
\subsubsection{Path queries}
|
||||||
|
|
||||||
Using a node array, we can also efficiently
|
Using a node array, we can also efficiently
|
||||||
process paths between the root node and any other
|
calculate sums of values on
|
||||||
|
paths between the root node and any other
|
||||||
node in the tree.
|
node in the tree.
|
||||||
Let us next consider a problem where our task
|
Let us next consider a problem where our task
|
||||||
is to support the following queries:
|
is to support the following queries:
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item change the value of node $x$
|
\item change the value of a node
|
||||||
\item calculate the sum of values from the root to node $x$
|
\item calculate the sum of values on a path between
|
||||||
|
the root node and a node
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
For example, in the following tree, the sum of
|
For example, in the following tree, the sum of
|
||||||
|
@ -428,10 +433,10 @@ To solve this problem, we can use a similar
|
||||||
technique as we used for subtree queries,
|
technique as we used for subtree queries,
|
||||||
but the values of the nodes are stored
|
but the values of the nodes are stored
|
||||||
in a special way:
|
in a special way:
|
||||||
if the value of a node at index $k$
|
if the value of a node at position $k$
|
||||||
increases by $a$,
|
increases by $a$,
|
||||||
the value at index $k$ increases by $a$
|
the value at position $k$ increases by $a$
|
||||||
and the value at index $k+c$ decreases by $a$,
|
and the value at position $k+c$ decreases by $a$,
|
||||||
where $c$ is the size of the subtree.
|
where $c$ is the size of the subtree.
|
||||||
|
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
|
@ -555,29 +560,29 @@ can be calculated as follows:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
The sum is
|
The sum is
|
||||||
\[4+5+3-5+2+5-2=12,\]
|
\[4+5+3-5+2+5-2=12\]
|
||||||
that equals the sum $4+5+3=12$.
|
that equals the sum $4+5+3=12$.
|
||||||
This method works because the value of each node
|
This method works, because the value of each node
|
||||||
is added to the sum when the depth-first search
|
is added to the sum when the depth-first search
|
||||||
visits it for the first time, and correspondingly,
|
visits the node for the first time, and the value
|
||||||
the value is removed from the sum when the subtree of the
|
of the node is removed from the sum when the subtree of the
|
||||||
node has been processed.
|
node has been processed.
|
||||||
|
|
||||||
Once again, we can store the values of the nodes
|
Once again, we can store the values of the nodes
|
||||||
in a binary indexed tree or a segment tree,
|
in a binary indexed tree or a segment tree,
|
||||||
so it is possible to both calculate the sum of values and
|
so it is possible to both update a value and
|
||||||
change a value efficiently in $O(\log n)$ time.
|
calculate the sum of values efficiently in $O(\log n)$ time.
|
||||||
|
|
||||||
\section{Lowest common ancestor}
|
\section{Lowest common ancestor}
|
||||||
|
|
||||||
\index{lowest common ancestor}
|
\index{lowest common ancestor}
|
||||||
|
|
||||||
The \key{lowest common ancestor}
|
The \key{lowest common ancestor}
|
||||||
of two nodes is a the lowest node in the tree
|
of two nodes in the tree is the lowest node
|
||||||
whose subtree contains both the nodes.
|
whose subtree contains both the nodes.
|
||||||
A typical problem is to efficiently process
|
A typical problem is to efficiently process
|
||||||
queries where the task is to find the lowest
|
queries that ask to find the lowest
|
||||||
common ancestor of two nodes.
|
common ancestor of given two nodes.
|
||||||
For example, in the tree
|
For example, in the tree
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
|
@ -606,15 +611,15 @@ finding the lowest common ancestor of two nodes.
|
||||||
|
|
||||||
\subsubsection{Method 1}
|
\subsubsection{Method 1}
|
||||||
|
|
||||||
One way to solve the problem is use the fact
|
One way to solve the problem is to use the fact
|
||||||
that we can efficiently find the $k$th
|
that we can efficiently find the $k$th
|
||||||
ancestor of any node in the tree.
|
ancestor of any node in the tree.
|
||||||
Using this idea, we can first ensure that
|
Thus, we can first make sure that
|
||||||
both nodes are at the same level in the tree,
|
both nodes are at the same level in the tree,
|
||||||
and then find the smallest value of $k$
|
and then find the smallest value of $k$
|
||||||
where the $k$th ancestor of both nodes is the same.
|
such that the $k$th ancestor of both nodes is the same.
|
||||||
|
|
||||||
As an example, let's find the lowest common
|
As an example, let us find the lowest common
|
||||||
ancestor of nodes $5$ and $8$ in the following tree:
|
ancestor of nodes $5$ and $8$ in the following tree:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -639,8 +644,8 @@ ancestor of nodes $5$ and $8$ in the following tree:
|
||||||
|
|
||||||
Node $5$ is at level $3$, while node $8$ is at level $4$.
|
Node $5$ is at level $3$, while node $8$ is at level $4$.
|
||||||
Thus, we first move one step upwards from node $8$ to node $6$.
|
Thus, we first move one step upwards from node $8$ to node $6$.
|
||||||
After this, it turns out that the parent of both node $5$
|
After this, it turns out that the parent of both nodes $5$
|
||||||
and node $6$ is node $2$, so we have found the lowest common ancestor.
|
and $6$ is node $2$, so we have found the lowest common ancestor.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
|
@ -669,13 +674,13 @@ and node $6$ is node $2$, so we have found the lowest common ancestor.
|
||||||
Using this method, we can find the lowest common ancestor
|
Using this method, we can find the lowest common ancestor
|
||||||
of any two nodes in $O(\log n)$ time after an $O(n \log n)$ time
|
of any two nodes in $O(\log n)$ time after an $O(n \log n)$ time
|
||||||
preprocessing, because both steps can be
|
preprocessing, because both steps can be
|
||||||
done in $O(\log n)$ time.
|
performed in $O(\log n)$ time.
|
||||||
|
|
||||||
\subsubsection{Method 2}
|
\subsubsection{Method 2}
|
||||||
|
|
||||||
Another way to solve the problem is based on
|
Another way to solve the problem is based on
|
||||||
a node array.
|
a node array.
|
||||||
Again, the idea is to traverse the nodes
|
Once again, the idea is to traverse the nodes
|
||||||
using a depth-first search:
|
using a depth-first search:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -716,12 +721,12 @@ using a depth-first search:
|
||||||
However, we add each node to the node array \emph{always}
|
However, we add each node to the node array \emph{always}
|
||||||
when the depth-first search visits the node,
|
when the depth-first search visits the node,
|
||||||
and not only at the first visit.
|
and not only at the first visit.
|
||||||
Thus, a node that has $k$ children appears $k+1$ times
|
Hence, a node that has $k$ children appears $k+1$ times
|
||||||
in the node array, and there are a total of $2n-1$
|
in the node array, and there are a total of $2n-1$
|
||||||
nodes in the array.
|
nodes in the array.
|
||||||
|
|
||||||
We store two values in the array:
|
We store two values in the array:
|
||||||
(1) identifier of the node, and (2) the level of the
|
(1) the identifier of the node, and (2) the level of the
|
||||||
node in the tree.
|
node in the tree.
|
||||||
The following array corresponds to the above tree:
|
The following array corresponds to the above tree:
|
||||||
|
|
||||||
|
@ -785,7 +790,7 @@ The following array corresponds to the above tree:
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Using this array, we can find the lowest common ancestor
|
Using this array, we can find the lowest common ancestor
|
||||||
of nodes $a$ and $b$ by locating the node with lowest level
|
of nodes $a$ and $b$ by finding the node with lowest level
|
||||||
between nodes $a$ and $b$ in the array.
|
between nodes $a$ and $b$ in the array.
|
||||||
For example, the lowest common ancestor of nodes $5$ and $8$
|
For example, the lowest common ancestor of nodes $5$ and $8$
|
||||||
can be found as follows:
|
can be found as follows:
|
||||||
|
@ -852,9 +857,9 @@ can be found as follows:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Node 5 is at index 3, node 8 is at index 6,
|
Node 5 is at position 3, node 8 is at position 6,
|
||||||
and the node with lowest level between
|
and the node with lowest level between
|
||||||
indices $3 \ldots 6$ is node 2 at index 4
|
positions $3 \ldots 6$ is node 2 at position 4
|
||||||
whose level is 2.
|
whose level is 2.
|
||||||
Thus, the lowest common ancestor of
|
Thus, the lowest common ancestor of
|
||||||
nodes 5 and 8 is node 2.
|
nodes 5 and 8 is node 2.
|
||||||
|
@ -863,23 +868,23 @@ Using a segment tree, we can find the lowest
|
||||||
common ancestor in $O(\log n)$ time.
|
common ancestor in $O(\log n)$ time.
|
||||||
Since the array is static, the time complexity
|
Since the array is static, the time complexity
|
||||||
$O(1)$ is also possible, but this is rarely needed.
|
$O(1)$ is also possible, but this is rarely needed.
|
||||||
In both cases, preprocessing takes $O(n \log n)$ time.
|
In both cases, the preprocessing takes $O(n \log n)$ time.
|
||||||
|
|
||||||
\subsubsection{Distances of nodes}
|
\subsubsection{Distances of nodes}
|
||||||
|
|
||||||
Finally, let's consider a problem where
|
Finally, let us consider a problem of
|
||||||
each query asks to find the distance between
|
finding the distance between
|
||||||
two nodes in the tree, i.e., the length of the
|
two nodes in the tree, which equals
|
||||||
path between them.
|
the length of the path between them.
|
||||||
It turns out that this problem reduces to
|
It turns out that this problem reduces to
|
||||||
finding the lowest common ancestor.
|
finding the lowest common ancestor of the nodes.
|
||||||
|
|
||||||
First, we choose an arbitrary node for the
|
First, we choose an arbitrary node for the
|
||||||
root of the tree.
|
root of the tree.
|
||||||
After this, the distance between nodes $a$ and $b$
|
After this, the distance between nodes $a$ and $b$
|
||||||
is $d(a)+d(b)-2 \cdot d(c)$,
|
is $d(a)+d(b)-2 \cdot d(c)$,
|
||||||
where $c$ is the lowest common ancestor,
|
where $c$ is the lowest common ancestor of $a$ and $b$
|
||||||
and $d(s)$ is the distance from the root node
|
and $d(s)$ denotes the distance from the root node
|
||||||
to node $s$.
|
to node $s$.
|
||||||
For example, in the tree
|
For example, in the tree
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue