diff --git a/chapter18.tex b/chapter18.tex index 859fe8e..b121514 100644 --- a/chapter18.tex +++ b/chapter18.tex @@ -939,7 +939,7 @@ processes each query before receiving the next query. However, in many problems, the online property is not necessary. In this section, we focus on \emph{offline} algorithms -that are given a collection of queries that can be +that are given a set of queries that can be processed in any order. It is often easier to design an offline algorithm compared to an online algorithm. @@ -1138,9 +1138,119 @@ when $a$ and $b$ are C++ standard library data structures. \subsubsection{Lowest common ancestors} -It turns out that we can also process a collection of -lowest common ancestor queries using an offline algorithm\footnote{This -algorithm was discovered by R. E. Tarjan in 1979 \cite{tar79}.}. -This algorithm is based on the union-find data structure -(see 15.2), and it is easier to implement than the -previous algorithms presented in this chapter. +There is also an offline algorithm +for processing a set of +lowest common ancestor queries\footnote{This +algorithm was published by R. E. Tarjan in 1979 \cite{tar79}.}. +The algorithm is based on the union-find data structure +(see Chapter 15.2), and the benefit of the algorithm is +that it is easier to implement than the +algorithms discussed earlier in this chapter. + +The algorithm is given as input a set of pairs of nodes, +and it determines for each such pair the +lowest common ancestor. +The algorithm performs a depth-first tree traversal +and maintains disjoint sets of nodes. +Initially, each node belongs to a separate set. +For each set, we also maintain the highest node in the +tree that belongs to the set. + +When the algorithm visits a node $x$, +it goes through all nodes $y$ such that +the lowest common ancestor of $x$ and $y$ +has to be found. +If $y$ has already been visited, +the algorithm reports that the +lowest common ancestor of $x$ and $y$ +is the highest node in the set of $y$. +Then, after processing the subtree of $x$, +the algorithm combines the sets of $x$ and its parent. + +For example, assume that we would like the lowest +common ancestor of node pairs $(5,8)$ +and $(2,7)$ in the following tree: +\begin{center} +\begin{tikzpicture}[scale=0.85] +\node[draw, circle] (1) at (0,3) {$1$}; +\node[draw, circle] (2) at (2,1) {$4$}; +\node[draw, circle] (3) at (-2,1) {$2$}; +\node[draw, circle] (4) at (0,1) {$3$}; +\node[draw, circle] (5) at (2,-1) {$7$}; +\node[draw, circle] (6) at (-3,-1) {$5$}; +\node[draw, circle] (7) at (-1,-1) {$6$}; +\node[draw, circle] (8) at (-1,-3) {$8$}; +\path[draw,thick,-] (1) -- (2); +\path[draw,thick,-] (1) -- (3); +\path[draw,thick,-] (1) -- (4); +\path[draw,thick,-] (2) -- (5); +\path[draw,thick,-] (3) -- (6); +\path[draw,thick,-] (3) -- (7); +\path[draw,thick,-] (7) -- (8); +\end{tikzpicture} +\end{center} + +In the following pictures, gray nodes denote visited nodes +and dashed groups of nodes belong to the same set. +When the algorithm visits node 8, it notices that +node 5 has been visited and the highest node +in its set is 2. Thus, the lowest common ancestor +of nodes 5 and 8 is 2: +\begin{center} +\begin{tikzpicture}[scale=0.85] +\node[draw, circle, fill=lightgray] (1) at (0,3) {$1$}; +\node[draw, circle] (2) at (2,1) {$4$}; +\node[draw, circle, fill=lightgray] (3) at (-2,1) {$2$}; +\node[draw, circle] (4) at (0,1) {$3$}; +\node[draw, circle] (5) at (2,-1) {$7$}; +\node[draw, circle, fill=lightgray] (6) at (-3,-1) {$5$}; +\node[draw, circle, fill=lightgray] (7) at (-1,-1) {$6$}; +\node[draw, circle, fill=gray] (8) at (-1,-3) {$8$}; +\path[draw,thick,-] (1) -- (2); +\path[draw,thick,-] (1) -- (3); +\path[draw,thick,-] (1) -- (4); +\path[draw,thick,-] (2) -- (5); +\path[draw,thick,-] (3) -- (6); +\path[draw,thick,-] (3) -- (7); +\path[draw,thick,-] (7) -- (8); + +\draw [red,thick,dashed,line width=2pt,rotate around={-28:(-2,0)}] (-2.9,1.5) rectangle (-1.9,-2); + + +\draw [red,thick,dashed,line width=2pt] (-1.5,-0.5) rectangle (-0.5,-1.5); +\draw [red,thick,dashed,line width=2pt] (-1.5,-2.5) rectangle (-0.5,-3.5); + +\draw [red,thick,dashed,line width=2pt] (0.5,3.5) rectangle (-0.5,2.5); +\draw [red,thick,dashed,line width=2pt] (0.5,1.5) rectangle (-0.5,0.5); +\draw [red,thick,dashed,line width=2pt] (2.5,1.5) rectangle (1.5,0.5); +\draw [red,thick,dashed,line width=2pt] (2.5,-0.5) rectangle (1.5,-1.5); +\end{tikzpicture} +\end{center} + +Later, when visiting node 7, +the algorithm determines that +the lowest common ancestor of nodes 2 and 7 is 1: +\begin{center} +\begin{tikzpicture}[scale=0.85] +\node[draw, circle, fill=lightgray] (1) at (0,3) {$1$}; +\node[draw, circle, fill=lightgray] (2) at (2,1) {$4$}; +\node[draw, circle, fill=lightgray] (3) at (-2,1) {$2$}; +\node[draw, circle, fill=lightgray] (4) at (0,1) {$3$}; +\node[draw, circle, fill=gray] (5) at (2,-1) {$7$}; +\node[draw, circle, fill=lightgray] (6) at (-3,-1) {$5$}; +\node[draw, circle, fill=lightgray] (7) at (-1,-1) {$6$}; +\node[draw, circle, fill=lightgray] (8) at (-1,-3) {$8$}; +\path[draw,thick,-] (1) -- (2); +\path[draw,thick,-] (1) -- (3); +\path[draw,thick,-] (1) -- (4); +\path[draw,thick,-] (2) -- (5); +\path[draw,thick,-] (3) -- (6); +\path[draw,thick,-] (3) -- (7); +\path[draw,thick,-] (7) -- (8); + +\draw [red,thick,dashed,line width=2pt] (0.5,3.5) rectangle (-3.5,-3.5); +\draw [red,thick,dashed,line width=2pt] (2.5,1.5) rectangle (1.5,0.5); +\draw [red,thick,dashed,line width=2pt] (2.5,-0.5) rectangle (1.5,-1.5); + +\end{tikzpicture} +\end{center} diff --git a/list.tex b/list.tex index e138b9d..7f849bb 100644 --- a/list.tex +++ b/list.tex @@ -352,6 +352,11 @@ Efficiency of a good but not linear set union algorithm. \emph{Journal of the ACM}, 22(2):215--225, 1975. +\bibitem{tar79} + R. E. Tarjan. + Applications of path compression on balanced trees. + \emph{Journal of the ACM}, 26(4):690--715, 1979. + \bibitem{tar84} R. E. Tarjan and U. Vishkin. Finding biconnected componemts and computing tree functions in logarithmic parallel time.