913 lines
27 KiB
TeX
913 lines
27 KiB
TeX
\chapter{Tree queries}
|
|
|
|
\index{puukysely@puukysely}
|
|
|
|
Tässä luvussa tutustumme algoritmeihin,
|
|
joiden avulla voi toteuttaa tehokkaasti kyselyitä
|
|
juurelliseen puuhun.
|
|
Kyselyt liittyvät puussa oleviin polkuihin
|
|
ja alipuihin.
|
|
Esimerkkejä kyselyistä ovat:
|
|
|
|
\begin{itemize}
|
|
\item mikä solmu on $k$ askelta ylempänä solmua $x$?
|
|
\item mikä on solmun $x$ alipuun arvojen summa?
|
|
\item mikä on solmujen $a$ ja $b$ välisen polun arvojen summa?
|
|
\item mikä on solmujen $a$ ja $b$ alin yhteinen esivanhempi?
|
|
\end{itemize}
|
|
|
|
\section{Tehokas nouseminen}
|
|
|
|
Tehokas nouseminen puussa tarkoittaa,
|
|
että voimme selvittää nopeasti,
|
|
mihin solmuun päätyy kulkemalla
|
|
$k$ askelta ylöspäin solmusta $x$ alkaen.
|
|
Merkitään $f(x,k)$ solmua,
|
|
joka on $k$ askelta ylempänä solmua $x$.
|
|
Esimerkiksi seuraavassa puussa
|
|
$f(2,1)=1$ ja $f(8,2)=4$.
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\node[draw, circle] (1) at (0,3) {$1$};
|
|
\node[draw, circle] (2) at (2,1) {$2$};
|
|
\node[draw, circle] (3) at (-2,1) {$4$};
|
|
\node[draw, circle] (4) at (0,1) {$5$};
|
|
\node[draw, circle] (5) at (2,-1) {$6$};
|
|
\node[draw, circle] (6) at (-3,-1) {$3$};
|
|
\node[draw, circle] (7) at (-1,-1) {$7$};
|
|
\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);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (8) edge [bend left] (3);
|
|
\path[draw=red,thick,->,line width=2pt] (2) edge [bend right] (1);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Suoraviivainen tapa laskea funktion $f(x,k)$
|
|
arvo on kulkea puussa $k$ askelta ylöspäin
|
|
solmusta $x$ alkaen.
|
|
Tämän aikavaativuus on kuitenkin $O(n)$,
|
|
koska on mahdollista, että puussa on
|
|
ketju, jossa on $O(n)$ solmua.
|
|
|
|
Kuten luvussa 16.3, funktion $f(x,k)$
|
|
arvo on mahdollista laskea tehokkaasti ajassa
|
|
$O(\log k)$ sopivan esikäsittelyn avulla.
|
|
Ideana on laskea etukäteen kaikki arvot
|
|
$f(x,k)$, joissa $k=1,2,4,8,\ldots$ eli 2:n potenssi.
|
|
Esimerkiksi yllä olevassa puussa muodostuu seuraava taulukko:
|
|
|
|
\begin{center}
|
|
\begin{tabular}{r|rrrrrrrrr}
|
|
$x$ & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
|
|
\hline
|
|
$f(x,1)$ & 0 & 1 & 4 & 1 & 1 & 2 & 4 & 7 \\
|
|
$f(x,2)$ & 0 & 0 & 1 & 0 & 0 & 1 & 1 & 4 \\
|
|
$f(x,4)$ & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
|
|
$\cdots$ \\
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
Taulukossa arvo 0 tarkoittaa, että nousemalla $k$
|
|
askelta päätyy puun ulkopuolelle juurisolmun yläpuolelle.
|
|
|
|
Esilaskenta vie aikaa $O(n \log n)$, koska jokaisesta
|
|
solmusta voi nousta korkeintaan $n$ askelta ylöspäin.
|
|
Tämän jälkeen minkä tahansa funktion $f(x,k)$ arvon saa
|
|
laskettua ajassa $O(\log k)$ jakamalla nousun 2:n
|
|
potenssin osiin.
|
|
|
|
\section{Solmutaulukko}
|
|
|
|
\index{solmutaulukko@solmutaulukko}
|
|
|
|
\key{Solmutaulukko} sisältää juurellisen puun solmut siinä
|
|
järjestyksessä kuin juuresta alkava syvyyshaku
|
|
vierailee solmuissa.
|
|
Esimerkiksi puussa
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\node[draw, circle] (1) at (0,3) {$1$};
|
|
\node[draw, circle] (2) at (-3,1) {$2$};
|
|
\node[draw, circle] (3) at (-1,1) {$3$};
|
|
\node[draw, circle] (4) at (1,1) {$4$};
|
|
\node[draw, circle] (5) at (3,1) {$5$};
|
|
\node[draw, circle] (6) at (-3,-1) {$6$};
|
|
\node[draw, circle] (7) at (-0.5,-1) {$7$};
|
|
\node[draw, circle] (8) at (1,-1) {$8$};
|
|
\node[draw, circle] (9) at (2.5,-1) {$9$};
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
\path[draw,thick,-] (1) -- (3);
|
|
\path[draw,thick,-] (1) -- (4);
|
|
\path[draw,thick,-] (1) -- (5);
|
|
\path[draw,thick,-] (2) -- (6);
|
|
\path[draw,thick,-] (4) -- (7);
|
|
\path[draw,thick,-] (4) -- (8);
|
|
\path[draw,thick,-] (4) -- (9);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
syvyyshaku etenee
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\node[draw, circle] (1) at (0,3) {$1$};
|
|
\node[draw, circle] (2) at (-3,1) {$2$};
|
|
\node[draw, circle] (3) at (-1,1) {$3$};
|
|
\node[draw, circle] (4) at (1,1) {$4$};
|
|
\node[draw, circle] (5) at (3,1) {$5$};
|
|
\node[draw, circle] (6) at (-3,-1) {$6$};
|
|
\node[draw, circle] (7) at (-0.5,-1) {$7$};
|
|
\node[draw, circle] (8) at (1,-1) {$8$};
|
|
\node[draw, circle] (9) at (2.5,-1) {$9$};
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
\path[draw,thick,-] (1) -- (3);
|
|
\path[draw,thick,-] (1) -- (4);
|
|
\path[draw,thick,-] (1) -- (5);
|
|
\path[draw,thick,-] (2) -- (6);
|
|
\path[draw,thick,-] (4) -- (7);
|
|
\path[draw,thick,-] (4) -- (8);
|
|
\path[draw,thick,-] (4) -- (9);
|
|
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (2);
|
|
\path[draw=red,thick,->,line width=2pt] (2) edge [bend right=15] (6);
|
|
\path[draw=red,thick,->,line width=2pt] (6) edge [bend right=15] (2);
|
|
\path[draw=red,thick,->,line width=2pt] (2) edge [bend right=15] (1);
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (3);
|
|
\path[draw=red,thick,->,line width=2pt] (3) edge [bend right=15] (1);
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (4);
|
|
\path[draw=red,thick,->,line width=2pt] (4) edge [bend right=15] (7);
|
|
\path[draw=red,thick,->,line width=2pt] (7) edge [bend right=15] (4);
|
|
\path[draw=red,thick,->,line width=2pt] (4) edge [bend right=15] (8);
|
|
\path[draw=red,thick,->,line width=2pt] (8) edge [bend right=15] (4);
|
|
\path[draw=red,thick,->,line width=2pt] (4) edge [bend right=15] (9);
|
|
\path[draw=red,thick,->,line width=2pt] (9) edge [bend right=15] (4);
|
|
\path[draw=red,thick,->,line width=2pt] (4) edge [bend right=15] (1);
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (5);
|
|
\path[draw=red,thick,->,line width=2pt] (5) edge [bend right=15] (1);
|
|
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
ja solmutaulukoksi tulee:
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\draw (0,0) grid (9,1);
|
|
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$6$};
|
|
\node at (3.5,0.5) {$3$};
|
|
\node at (4.5,0.5) {$4$};
|
|
\node at (5.5,0.5) {$7$};
|
|
\node at (6.5,0.5) {$8$};
|
|
\node at (7.5,0.5) {$9$};
|
|
\node at (8.5,0.5) {$5$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,1.4) {$1$};
|
|
\node at (1.5,1.4) {$2$};
|
|
\node at (2.5,1.4) {$3$};
|
|
\node at (3.5,1.4) {$4$};
|
|
\node at (4.5,1.4) {$5$};
|
|
\node at (5.5,1.4) {$6$};
|
|
\node at (6.5,1.4) {$7$};
|
|
\node at (7.5,1.4) {$8$};
|
|
\node at (8.5,1.4) {$9$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
\subsubsection{Alipuiden käsittely}
|
|
|
|
Solmutaulukossa jokaisen alipuun kaikki solmut ovat peräkkäin
|
|
niin, että ensin on alipuun juurisolmu ja sitten
|
|
kaikki muut alipuun solmut.
|
|
Esimerkiksi äskeisessä taulukossa solmun $4$
|
|
alipuuta vastaa seuraava taulukon osa:
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\fill[color=lightgray] (4,0) rectangle (8,1);
|
|
\draw (0,0) grid (9,1);
|
|
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$6$};
|
|
\node at (3.5,0.5) {$3$};
|
|
\node at (4.5,0.5) {$4$};
|
|
\node at (5.5,0.5) {$7$};
|
|
\node at (6.5,0.5) {$8$};
|
|
\node at (7.5,0.5) {$9$};
|
|
\node at (8.5,0.5) {$5$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,1.4) {$1$};
|
|
\node at (1.5,1.4) {$2$};
|
|
\node at (2.5,1.4) {$3$};
|
|
\node at (3.5,1.4) {$4$};
|
|
\node at (4.5,1.4) {$5$};
|
|
\node at (5.5,1.4) {$6$};
|
|
\node at (6.5,1.4) {$7$};
|
|
\node at (7.5,1.4) {$8$};
|
|
\node at (8.5,1.4) {$9$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
Tämän ansiosta solmutaulukon avulla voi käsitellä tehokkaasti
|
|
puun alipuihin liittyviä kyselyitä.
|
|
Ratkaistaan esimerkkinä tehtävä,
|
|
jossa kuhunkin puun solmuun liittyy
|
|
arvo ja toteutettavana on seuraavat kyselyt:
|
|
\begin{itemize}
|
|
\item muuta solmun $x$ arvoa
|
|
\item laske arvojen summa solmun $x$ alipuussa
|
|
\end{itemize}
|
|
|
|
Tarkastellaan seuraavaa puuta,
|
|
jossa siniset luvut ovat solmujen arvoja.
|
|
Esimerkiksi solmun $4$ alipuun arvojen summa on $3+4+3+1=11$.
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\node[draw, circle] (1) at (0,3) {$1$};
|
|
\node[draw, circle] (2) at (-3,1) {$2$};
|
|
\node[draw, circle] (3) at (-1,1) {$3$};
|
|
\node[draw, circle] (4) at (1,1) {$4$};
|
|
\node[draw, circle] (5) at (3,1) {$5$};
|
|
\node[draw, circle] (6) at (-3,-1) {$6$};
|
|
\node[draw, circle] (7) at (-0.5,-1) {$7$};
|
|
\node[draw, circle] (8) at (1,-1) {$8$};
|
|
\node[draw, circle] (9) at (2.5,-1) {$9$};
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
\path[draw,thick,-] (1) -- (3);
|
|
\path[draw,thick,-] (1) -- (4);
|
|
\path[draw,thick,-] (1) -- (5);
|
|
\path[draw,thick,-] (2) -- (6);
|
|
\path[draw,thick,-] (4) -- (7);
|
|
\path[draw,thick,-] (4) -- (8);
|
|
\path[draw,thick,-] (4) -- (9);
|
|
|
|
\node[color=blue] at (0,3+0.65) {2};
|
|
\node[color=blue] at (-3-0.65,1) {3};
|
|
\node[color=blue] at (-1-0.65,1) {5};
|
|
\node[color=blue] at (1+0.65,1) {3};
|
|
\node[color=blue] at (3+0.65,1) {1};
|
|
\node[color=blue] at (-3,-1-0.65) {4};
|
|
\node[color=blue] at (-0.5,-1-0.65) {4};
|
|
\node[color=blue] at (1,-1-0.65) {3};
|
|
\node[color=blue] at (2.5,-1-0.65) {1};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Ideana on luoda solmutaulukko, joka sisältää jokaisesta solmusta
|
|
kolme tietoa: (1) solmun tunnus, (2) alipuun koko ja (3) solmun arvo.
|
|
Esimerkiksi yllä olevasta puusta syntyy seuraava taulukko:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\draw (0,1) grid (9,-2);
|
|
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$6$};
|
|
\node at (3.5,0.5) {$3$};
|
|
\node at (4.5,0.5) {$4$};
|
|
\node at (5.5,0.5) {$7$};
|
|
\node at (6.5,0.5) {$8$};
|
|
\node at (7.5,0.5) {$9$};
|
|
\node at (8.5,0.5) {$5$};
|
|
|
|
\node at (0.5,-0.5) {$9$};
|
|
\node at (1.5,-0.5) {$2$};
|
|
\node at (2.5,-0.5) {$1$};
|
|
\node at (3.5,-0.5) {$1$};
|
|
\node at (4.5,-0.5) {$4$};
|
|
\node at (5.5,-0.5) {$1$};
|
|
\node at (6.5,-0.5) {$1$};
|
|
\node at (7.5,-0.5) {$1$};
|
|
\node at (8.5,-0.5) {$1$};
|
|
|
|
\node at (0.5,-1.5) {$2$};
|
|
\node at (1.5,-1.5) {$3$};
|
|
\node at (2.5,-1.5) {$4$};
|
|
\node at (3.5,-1.5) {$5$};
|
|
\node at (4.5,-1.5) {$3$};
|
|
\node at (5.5,-1.5) {$4$};
|
|
\node at (6.5,-1.5) {$3$};
|
|
\node at (7.5,-1.5) {$1$};
|
|
\node at (8.5,-1.5) {$1$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,1.4) {$1$};
|
|
\node at (1.5,1.4) {$2$};
|
|
\node at (2.5,1.4) {$3$};
|
|
\node at (3.5,1.4) {$4$};
|
|
\node at (4.5,1.4) {$5$};
|
|
\node at (5.5,1.4) {$6$};
|
|
\node at (6.5,1.4) {$7$};
|
|
\node at (7.5,1.4) {$8$};
|
|
\node at (8.5,1.4) {$9$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Tästä taulukosta alipuun solmujen arvojen summa selviää
|
|
lukemalla ensin alipuun koko ja sitten sitä vastaavat solmut.
|
|
Esimerkiksi solmun $4$ alipuun arvojen summa selviää näin:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\fill[color=lightgray] (4,1) rectangle (5,0);
|
|
\fill[color=lightgray] (4,0) rectangle (5,-1);
|
|
\fill[color=lightgray] (4,-1) rectangle (8,-2);
|
|
\draw (0,1) grid (9,-2);
|
|
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$6$};
|
|
\node at (3.5,0.5) {$3$};
|
|
\node at (4.5,0.5) {$4$};
|
|
\node at (5.5,0.5) {$7$};
|
|
\node at (6.5,0.5) {$8$};
|
|
\node at (7.5,0.5) {$9$};
|
|
\node at (8.5,0.5) {$5$};
|
|
|
|
\node at (0.5,-0.5) {$9$};
|
|
\node at (1.5,-0.5) {$2$};
|
|
\node at (2.5,-0.5) {$1$};
|
|
\node at (3.5,-0.5) {$1$};
|
|
\node at (4.5,-0.5) {$4$};
|
|
\node at (5.5,-0.5) {$1$};
|
|
\node at (6.5,-0.5) {$1$};
|
|
\node at (7.5,-0.5) {$1$};
|
|
\node at (8.5,-0.5) {$1$};
|
|
|
|
\node at (0.5,-1.5) {$2$};
|
|
\node at (1.5,-1.5) {$3$};
|
|
\node at (2.5,-1.5) {$4$};
|
|
\node at (3.5,-1.5) {$5$};
|
|
\node at (4.5,-1.5) {$3$};
|
|
\node at (5.5,-1.5) {$4$};
|
|
\node at (6.5,-1.5) {$3$};
|
|
\node at (7.5,-1.5) {$1$};
|
|
\node at (8.5,-1.5) {$1$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,1.4) {$1$};
|
|
\node at (1.5,1.4) {$2$};
|
|
\node at (2.5,1.4) {$3$};
|
|
\node at (3.5,1.4) {$4$};
|
|
\node at (4.5,1.4) {$5$};
|
|
\node at (5.5,1.4) {$6$};
|
|
\node at (6.5,1.4) {$7$};
|
|
\node at (7.5,1.4) {$8$};
|
|
\node at (8.5,1.4) {$9$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Viimeinen tarvittava askel on tallentaa solmujen arvot
|
|
binääri-indeksi\-puuhun tai segmenttipuuhun.
|
|
Tällöin sekä alipuun arvojen summan laskeminen
|
|
että solmun arvon muuttaminen onnistuvat ajassa $O(\log n)$,
|
|
eli pystymme vastaamaan kyselyihin tehokkaasti.
|
|
|
|
\subsubsection{Polkujen käsittely}
|
|
|
|
Solmutaulukon avulla voi myös käsitellä tehokkaasti
|
|
polkuja, jotka kulkevat juuresta tiettyyn solmuun puussa.
|
|
Ratkaistaan seuraavaksi tehtävä,
|
|
jossa toteutettavana on seuraavat kyselyt:
|
|
\begin{itemize}
|
|
\item muuta solmun $x$ arvoa
|
|
\item laske arvojen summa juuresta solmuun $x$
|
|
\end{itemize}
|
|
|
|
Esimerkiksi seuraavassa puussa polulla solmusta 1
|
|
solmuun 8 arvojen summa on $4+5+3=12$.
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\node[draw, circle] (1) at (0,3) {$1$};
|
|
\node[draw, circle] (2) at (-3,1) {$2$};
|
|
\node[draw, circle] (3) at (-1,1) {$3$};
|
|
\node[draw, circle] (4) at (1,1) {$4$};
|
|
\node[draw, circle] (5) at (3,1) {$5$};
|
|
\node[draw, circle] (6) at (-3,-1) {$6$};
|
|
\node[draw, circle] (7) at (-0.5,-1) {$7$};
|
|
\node[draw, circle] (8) at (1,-1) {$8$};
|
|
\node[draw, circle] (9) at (2.5,-1) {$9$};
|
|
|
|
\path[draw,thick,-] (1) -- (2);
|
|
\path[draw,thick,-] (1) -- (3);
|
|
\path[draw,thick,-] (1) -- (4);
|
|
\path[draw,thick,-] (1) -- (5);
|
|
\path[draw,thick,-] (2) -- (6);
|
|
\path[draw,thick,-] (4) -- (7);
|
|
\path[draw,thick,-] (4) -- (8);
|
|
\path[draw,thick,-] (4) -- (9);
|
|
|
|
\node[color=blue] at (0,3+0.65) {4};
|
|
\node[color=blue] at (-3-0.65,1) {5};
|
|
\node[color=blue] at (-1-0.65,1) {3};
|
|
\node[color=blue] at (1+0.65,1) {5};
|
|
\node[color=blue] at (3+0.65,1) {2};
|
|
\node[color=blue] at (-3,-1-0.65) {3};
|
|
\node[color=blue] at (-0.5,-1-0.65) {5};
|
|
\node[color=blue] at (1,-1-0.65) {3};
|
|
\node[color=blue] at (2.5,-1-0.65) {1};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Ideana on muodostaa samanlainen taulukko kuin
|
|
alipuiden käsittelyssä mutta tallentaa
|
|
solmujen arvot erikoisella tavalla:
|
|
kun taulukon kohdassa $k$ olevan solmun arvo on $a$,
|
|
kohdan $k$ arvoon lisätään $a$ ja kohdan $k+c$ arvosta
|
|
vähennetään $a$, missä $c$ on alipuun koko.
|
|
|
|
\begin{samepage}
|
|
Esimerkiksi yllä olevaa puuta vastaa seuraava taulukko:
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\draw (0,1) grid (10,-2);
|
|
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$6$};
|
|
\node at (3.5,0.5) {$3$};
|
|
\node at (4.5,0.5) {$4$};
|
|
\node at (5.5,0.5) {$7$};
|
|
\node at (6.5,0.5) {$8$};
|
|
\node at (7.5,0.5) {$9$};
|
|
\node at (8.5,0.5) {$5$};
|
|
\node at (9.5,0.5) {--};
|
|
|
|
\node at (0.5,-0.5) {$9$};
|
|
\node at (1.5,-0.5) {$2$};
|
|
\node at (2.5,-0.5) {$1$};
|
|
\node at (3.5,-0.5) {$1$};
|
|
\node at (4.5,-0.5) {$4$};
|
|
\node at (5.5,-0.5) {$1$};
|
|
\node at (6.5,-0.5) {$1$};
|
|
\node at (7.5,-0.5) {$1$};
|
|
\node at (8.5,-0.5) {$1$};
|
|
\node at (9.5,-0.5) {--};
|
|
|
|
\node at (0.5,-1.5) {$4$};
|
|
\node at (1.5,-1.5) {$5$};
|
|
\node at (2.5,-1.5) {$3$};
|
|
\node at (3.5,-1.5) {$-5$};
|
|
\node at (4.5,-1.5) {$2$};
|
|
\node at (5.5,-1.5) {$5$};
|
|
\node at (6.5,-1.5) {$-2$};
|
|
\node at (7.5,-1.5) {$-2$};
|
|
\node at (8.5,-1.5) {$-4$};
|
|
\node at (9.5,-1.5) {$-4$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,1.4) {$1$};
|
|
\node at (1.5,1.4) {$2$};
|
|
\node at (2.5,1.4) {$3$};
|
|
\node at (3.5,1.4) {$4$};
|
|
\node at (4.5,1.4) {$5$};
|
|
\node at (5.5,1.4) {$6$};
|
|
\node at (6.5,1.4) {$7$};
|
|
\node at (7.5,1.4) {$8$};
|
|
\node at (8.5,1.4) {$9$};
|
|
\node at (9.5,1.4) {$10$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
\end{samepage}
|
|
|
|
Esimerkiksi solmun $3$ arvona on $-5$, koska
|
|
se on solmujen $2$ ja $6$ alipuiden jälkeinen solmu,
|
|
mistä tulee arvoa $-5-3$, ja sen oma arvo on $3$.
|
|
Yhteensä solmun 3 arvo on siis $-5-3+3=-5$.
|
|
Huomaa, että taulukossa on ylimääräinen kohta 10,
|
|
johon on tallennettu vain juuren arvon vastaluku.
|
|
|
|
Nyt solmujen arvojen summa polulla juuresta alkaen
|
|
selviää laskemalla kaikkien taulukon arvojen summa
|
|
taulukon alusta solmuun asti.
|
|
Esimerkiksi summa solmusta $1$ solmuun $8$ selviää näin:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\fill[color=lightgray] (6,1) rectangle (7,0);
|
|
\fill[color=lightgray] (0,-1) rectangle (7,-2);
|
|
\draw (0,1) grid (10,-2);
|
|
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$6$};
|
|
\node at (3.5,0.5) {$3$};
|
|
\node at (4.5,0.5) {$4$};
|
|
\node at (5.5,0.5) {$7$};
|
|
\node at (6.5,0.5) {$8$};
|
|
\node at (7.5,0.5) {$9$};
|
|
\node at (8.5,0.5) {$5$};
|
|
\node at (9.5,0.5) {--};
|
|
|
|
\node at (0.5,-0.5) {$9$};
|
|
\node at (1.5,-0.5) {$2$};
|
|
\node at (2.5,-0.5) {$1$};
|
|
\node at (3.5,-0.5) {$1$};
|
|
\node at (4.5,-0.5) {$4$};
|
|
\node at (5.5,-0.5) {$1$};
|
|
\node at (6.5,-0.5) {$1$};
|
|
\node at (7.5,-0.5) {$1$};
|
|
\node at (8.5,-0.5) {$1$};
|
|
\node at (9.5,-0.5) {--};
|
|
|
|
\node at (0.5,-1.5) {$4$};
|
|
\node at (1.5,-1.5) {$5$};
|
|
\node at (2.5,-1.5) {$3$};
|
|
\node at (3.5,-1.5) {$-5$};
|
|
\node at (4.5,-1.5) {$2$};
|
|
\node at (5.5,-1.5) {$5$};
|
|
\node at (6.5,-1.5) {$-2$};
|
|
\node at (7.5,-1.5) {$-2$};
|
|
\node at (8.5,-1.5) {$-4$};
|
|
\node at (9.5,-1.5) {$-4$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,1.4) {$1$};
|
|
\node at (1.5,1.4) {$2$};
|
|
\node at (2.5,1.4) {$3$};
|
|
\node at (3.5,1.4) {$4$};
|
|
\node at (4.5,1.4) {$5$};
|
|
\node at (5.5,1.4) {$6$};
|
|
\node at (6.5,1.4) {$7$};
|
|
\node at (7.5,1.4) {$8$};
|
|
\node at (8.5,1.4) {$9$};
|
|
\node at (9.5,1.4) {$10$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Summaksi tulee
|
|
\[4+5+3-5+2+5-2=12,\]
|
|
mikä vastaa polun summaa $4+5+3=12$.
|
|
Tämä laskentatapa toimii, koska jokaisen solmun arvo
|
|
lisätään summaan, kun se tulee vastaan syvyyshaussa,
|
|
ja vähennetään summasta, kun sen käsittely päättyy.
|
|
|
|
Alipuiden käsittelyä vastaavasti voimme tallentaa
|
|
arvot binääri-indeksi\-puuhun tai segmenttipuuhun ja
|
|
sekä polun summan laskeminen että arvon muuttaminen
|
|
onnistuvat ajassa $O(\log n)$.
|
|
|
|
\section{Alin yhteinen esivanhempi}
|
|
|
|
\index{alin yhteinen esivanhempi@alin yhteinen esivanhempi}
|
|
|
|
Kahden puun solmun
|
|
\key{alin yhteinen esivanhempi}
|
|
on mahdollisimman matalalla puussa oleva solmu,
|
|
jonka alipuuhun kumpikin solmu kuuluu.
|
|
Tyypillinen tehtävä on vastata tehokkaasti
|
|
joukkoon kyselyitä, jossa selvitettävänä on
|
|
kahden solmun alin yhteinen esivanhempi.
|
|
Esimerkiksi puussa
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\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}
|
|
solmujen 5 ja 8 alin yhteinen esivanhempi on solmu 2
|
|
ja solmujen 3 ja 4 alin yhteinen esivanhempi on solmu 1.
|
|
|
|
Tutustumme seuraavaksi kahteen tehokkaaseen menetelmään
|
|
alimman yhteisen esivanhemman selvittämiseen.
|
|
|
|
\subsubsection{Menetelmä 1}
|
|
|
|
Yksi tapa ratkaista tehtävä on hyödyntää
|
|
tehokasta nousemista puussa.
|
|
Tällöin alimman yhteisen esivanhemman etsiminen
|
|
muodostuu kahdesta vaiheesta.
|
|
Ensin noustaan alemmasta solmusta samalle tasolle
|
|
ylemmän solmun kanssa,
|
|
sitten noustaan rinnakkain kohti
|
|
alinta yhteistä esivanhempaa.
|
|
|
|
Tarkastellaan esimerkkinä solmujen 5 ja 8
|
|
alimman yhteisen esivanhemman etsimistä:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\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,fill=lightgray] (6) at (-3,-1) {$5$};
|
|
\node[draw, circle] (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);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Solmu 5 on tasolla 3, kun taas solmu 8 on tasolla 4.
|
|
Niinpä nousemme ensin solmusta 8 yhden tason ylemmäs solmuun 6.
|
|
Tämän jälkeen nousemme rinnakkain solmuista 5 ja 6
|
|
lähtien yhden tason, jolloin päädymme solmuun 2:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\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,fill=lightgray] (6) at (-3,-1) {$5$};
|
|
\node[draw, circle] (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);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (6) edge [bend left] (3);
|
|
\path[draw=red,thick,->,line width=2pt] (8) edge [bend right] (7);
|
|
\path[draw=red,thick,->,line width=2pt] (7) edge [bend right] (3);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Menetelmä vaatii $O(n \log n)$-aikaisen esikäsittelyn,
|
|
jonka jälkeen minkä tahansa kahden solmun alin yhteinen
|
|
esivanhempi selviää ajassa $O(\log n)$,
|
|
koska kumpikin vaihe nousussa vie aikaa $O(\log n)$.
|
|
|
|
\subsubsection{Menetelmä 2}
|
|
|
|
Toinen tapa ratkaista tehtävä perustuu solmutaulukon
|
|
käyttämiseen.
|
|
Ideana on jälleen järjestää solmut syvyyshaun mukaan:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\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);
|
|
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (3);
|
|
\path[draw=red,thick,->,line width=2pt] (3) edge [bend right=15] (6);
|
|
\path[draw=red,thick,->,line width=2pt] (6) edge [bend right=15] (3);
|
|
\path[draw=red,thick,->,line width=2pt] (3) edge [bend right=15] (7);
|
|
\path[draw=red,thick,->,line width=2pt] (7) edge [bend right=15] (8);
|
|
\path[draw=red,thick,->,line width=2pt] (8) edge [bend right=15] (7);
|
|
\path[draw=red,thick,->,line width=2pt] (7) edge [bend right=15] (3);
|
|
\path[draw=red,thick,->,line width=2pt] (3) edge [bend right=15] (1);
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (4);
|
|
\path[draw=red,thick,->,line width=2pt] (4) edge [bend right=15] (1);
|
|
\path[draw=red,thick,->,line width=2pt] (1) edge [bend right=15] (2);
|
|
\path[draw=red,thick,->,line width=2pt] (2) edge [bend right=15] (5);
|
|
\path[draw=red,thick,->,line width=2pt] (5) edge [bend right=15] (2);
|
|
\path[draw=red,thick,->,line width=2pt] (2) edge [bend right=15] (1);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Erona aiempaan solmu lisätään kuitenkin solmutaulukkoon
|
|
mukaan \textit{aina}, kun syvyyshaku käy solmussa,
|
|
eikä vain ensimmäisellä kerralla.
|
|
Niinpä solmu esiintyy solmutaulukossa $k+1$ kertaa,
|
|
missä $k$ on solmun lasten määrä,
|
|
ja solmutaulukossa on yhteensä $2n-1$ solmua.
|
|
|
|
Tallennamme solmutaulukkoon kaksi tietoa:
|
|
(1) solmun tunnus ja (2) solmun taso puussa.
|
|
Esimerkkipuuta vastaavat taulukot ovat:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
|
|
\draw (0,1) grid (15,2);
|
|
%\node at (-1.1,1.5) {\texttt{node}};
|
|
\node at (0.5,1.5) {$1$};
|
|
\node at (1.5,1.5) {$2$};
|
|
\node at (2.5,1.5) {$5$};
|
|
\node at (3.5,1.5) {$2$};
|
|
\node at (4.5,1.5) {$6$};
|
|
\node at (5.5,1.5) {$8$};
|
|
\node at (6.5,1.5) {$6$};
|
|
\node at (7.5,1.5) {$2$};
|
|
\node at (8.5,1.5) {$1$};
|
|
\node at (9.5,1.5) {$3$};
|
|
\node at (10.5,1.5) {$1$};
|
|
\node at (11.5,1.5) {$4$};
|
|
\node at (12.5,1.5) {$7$};
|
|
\node at (13.5,1.5) {$4$};
|
|
\node at (14.5,1.5) {$1$};
|
|
|
|
|
|
\draw (0,0) grid (15,1);
|
|
%\node at (-1.1,0.5) {\texttt{depth}};
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$3$};
|
|
\node at (3.5,0.5) {$2$};
|
|
\node at (4.5,0.5) {$3$};
|
|
\node at (5.5,0.5) {$4$};
|
|
\node at (6.5,0.5) {$3$};
|
|
\node at (7.5,0.5) {$2$};
|
|
\node at (8.5,0.5) {$1$};
|
|
\node at (9.5,0.5) {$2$};
|
|
\node at (10.5,0.5) {$1$};
|
|
\node at (11.5,0.5) {$2$};
|
|
\node at (12.5,0.5) {$3$};
|
|
\node at (13.5,0.5) {$2$};
|
|
\node at (14.5,0.5) {$1$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,2.5) {$1$};
|
|
\node at (1.5,2.5) {$2$};
|
|
\node at (2.5,2.5) {$3$};
|
|
\node at (3.5,2.5) {$4$};
|
|
\node at (4.5,2.5) {$5$};
|
|
\node at (5.5,2.5) {$6$};
|
|
\node at (6.5,2.5) {$7$};
|
|
\node at (7.5,2.5) {$8$};
|
|
\node at (8.5,2.5) {$9$};
|
|
\node at (9.5,2.5) {$10$};
|
|
\node at (10.5,2.5) {$11$};
|
|
\node at (11.5,2.5) {$12$};
|
|
\node at (12.5,2.5) {$13$};
|
|
\node at (13.5,2.5) {$14$};
|
|
\node at (14.5,2.5) {$15$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Tämän taulukon avulla solmujen $a$ ja $b$ alin yhteinen esivanhempi
|
|
selviää etsimällä taulukosta alimman tason solmu
|
|
solmujen $a$ ja $b$ välissä.
|
|
Esimerkiksi solmujen 5 ja 8 alin yhteinen esivanhempi
|
|
löytyy seuraavasti:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.7]
|
|
\fill[color=lightgray] (2,1) rectangle (3,2);
|
|
\fill[color=lightgray] (5,1) rectangle (6,2);
|
|
\fill[color=lightgray] (2,0) rectangle (6,1);
|
|
|
|
\node at (3.5,-0.5) {$\uparrow$};
|
|
|
|
\draw (0,1) grid (15,2);
|
|
\node at (0.5,1.5) {$1$};
|
|
\node at (1.5,1.5) {$2$};
|
|
\node at (2.5,1.5) {$5$};
|
|
\node at (3.5,1.5) {$2$};
|
|
\node at (4.5,1.5) {$6$};
|
|
\node at (5.5,1.5) {$8$};
|
|
\node at (6.5,1.5) {$6$};
|
|
\node at (7.5,1.5) {$2$};
|
|
\node at (8.5,1.5) {$1$};
|
|
\node at (9.5,1.5) {$3$};
|
|
\node at (10.5,1.5) {$1$};
|
|
\node at (11.5,1.5) {$4$};
|
|
\node at (12.5,1.5) {$7$};
|
|
\node at (13.5,1.5) {$4$};
|
|
\node at (14.5,1.5) {$1$};
|
|
|
|
|
|
\draw (0,0) grid (15,1);
|
|
\node at (0.5,0.5) {$1$};
|
|
\node at (1.5,0.5) {$2$};
|
|
\node at (2.5,0.5) {$3$};
|
|
\node at (3.5,0.5) {$2$};
|
|
\node at (4.5,0.5) {$3$};
|
|
\node at (5.5,0.5) {$4$};
|
|
\node at (6.5,0.5) {$3$};
|
|
\node at (7.5,0.5) {$2$};
|
|
\node at (8.5,0.5) {$1$};
|
|
\node at (9.5,0.5) {$2$};
|
|
\node at (10.5,0.5) {$1$};
|
|
\node at (11.5,0.5) {$2$};
|
|
\node at (12.5,0.5) {$3$};
|
|
\node at (13.5,0.5) {$2$};
|
|
\node at (14.5,0.5) {$1$};
|
|
|
|
\footnotesize
|
|
\node at (0.5,2.5) {$1$};
|
|
\node at (1.5,2.5) {$2$};
|
|
\node at (2.5,2.5) {$3$};
|
|
\node at (3.5,2.5) {$4$};
|
|
\node at (4.5,2.5) {$5$};
|
|
\node at (5.5,2.5) {$6$};
|
|
\node at (6.5,2.5) {$7$};
|
|
\node at (7.5,2.5) {$8$};
|
|
\node at (8.5,2.5) {$9$};
|
|
\node at (9.5,2.5) {$10$};
|
|
\node at (10.5,2.5) {$11$};
|
|
\node at (11.5,2.5) {$12$};
|
|
\node at (12.5,2.5) {$13$};
|
|
\node at (13.5,2.5) {$14$};
|
|
\node at (14.5,2.5) {$15$};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
Solmu 5 on taulukossa kohdassa 3,
|
|
solmu 8 on taulukossa kohdassa 6
|
|
ja alimman tason solmu välillä $3 \ldots 6$
|
|
on kohdassa 4 oleva solmu 2,
|
|
jonka taso on 2.
|
|
Niinpä solmujen 5 ja 8 alin yhteinen esivanhempi
|
|
on solmu 2.
|
|
|
|
Alimman tason solmu välillä selviää
|
|
ajassa $O(\log n)$, kun taulukon sisältö on
|
|
tallennettu segmenttipuuhun.
|
|
Myös aikavaativuus $O(1)$ on mahdollinen,
|
|
koska taulukko on staattinen, mutta tälle on harvoin tarvetta.
|
|
Kummassakin tapauksessa esikäsittely vie aikaa $O(n \log n)$.
|
|
|
|
\subsubsection{Solmujen etäisyydet}
|
|
|
|
Tarkastellaan lopuksi tehtävää,
|
|
jossa kyselyissä tulee laskea tehokkaasti
|
|
kahden solmun etäisyys eli solmujen välisen polun pituus puussa.
|
|
Osoittautuu, että tämä tehtävä
|
|
palautuu alimman yhteisen esivanhemman etsimiseen.
|
|
|
|
Valitaan ensin mikä tahansa
|
|
solmu puun juureksi.
|
|
Tämän jälkeen solmujen $a$ ja $b$
|
|
etäisyys on $d(a)+d(b)-2 \cdot d(c)$,
|
|
missä $c$ on solmujen alin yhteinen esivanhempi
|
|
ja $d(s)$ on etäisyys puun juuresta solmuun $s$.
|
|
Esimerkiksi puussa
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[scale=0.9]
|
|
\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);
|
|
|
|
\path[draw=red,thick,-,line width=2pt] (8) -- node[font=\small] {} (7);
|
|
\path[draw=red,thick,-,line width=2pt] (7) -- node[font=\small] {} (3);
|
|
\path[draw=red,thick,-,line width=2pt] (6) -- node[font=\small] {} (3);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
solmujen 5 ja 8 alin yhteinen esivanhempi on 2.
|
|
Polku solmusta 5 solmuun 8
|
|
kulkee ensin ylöspäin solmusta 5
|
|
solmuun 2 ja sitten alaspäin
|
|
solmusta 2 solmuun 8.
|
|
Solmujen etäisyydet juuresta ovat $d(5)=3$,
|
|
$d(8)=4$ ja $d(2)=2$,
|
|
joten solmujen 5 ja 8 etäisyys
|
|
on $3+4-2\cdot2=3$.
|
|
|
|
|