556 lines
17 KiB
TeX
556 lines
17 KiB
TeX
|
\chapter{Puiden käsittely}
|
||
|
|
||
|
\index{puu@puu}
|
||
|
|
||
|
\key{Puu} on yhtenäinen, syklitön verkko,
|
||
|
jossa on $n$ solmua ja $n-1$ kaarta.
|
||
|
Jos puusta poistaa yhden kaaren, se ei ole enää yhtenäinen,
|
||
|
ja jos puuhun lisää yhden kaaren, se ei ole enää syklitön.
|
||
|
Puussa pätee myös aina, että
|
||
|
jokaisen kahden puun solmun välillä on yksikäsitteinen polku.
|
||
|
|
||
|
Esimerkiksi seuraavassa puussa on 7 solmua ja 6 kaarta:
|
||
|
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (0,3) {$1$};
|
||
|
\node[draw, circle] (2) at (2,3) {$2$};
|
||
|
\node[draw, circle] (3) at (0,1) {$4$};
|
||
|
\node[draw, circle] (4) at (2,1) {$5$};
|
||
|
\node[draw, circle] (5) at (4,1) {$6$};
|
||
|
\node[draw, circle] (6) at (-2,3) {$7$};
|
||
|
\node[draw, circle] (7) at (-2,1) {$3$};
|
||
|
\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);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
\index{lehti@lehti}
|
||
|
|
||
|
Puun \key{lehdet} ovat solmut,
|
||
|
joiden aste on 1 eli joista lähtee vain yksi kaari.
|
||
|
Esimerkiksi yllä olevan puun lehdet ovat
|
||
|
solmut 3, 5, 6 ja 7.
|
||
|
|
||
|
\index{juuri@juuri}
|
||
|
\index{juurellinen puu@juurellinen puu}
|
||
|
|
||
|
Jos puu on \key{juurellinen}, yksi solmuista
|
||
|
on puun \key{juuri},
|
||
|
jonka alapuolelle muut solmut asettuvat.
|
||
|
Esimerkiksi jos yllä olevassa puussa valitaan
|
||
|
juureksi solmu 1, solmut asettuvat seuraavaan järjestykseen:
|
||
|
|
||
|
\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$};
|
||
|
\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);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
\index{lapsi@lapsi}
|
||
|
\index{vanhempi@vanhempi}
|
||
|
|
||
|
Juurellisessa puussa solmun \key{lapset}
|
||
|
ovat sen alemman tason naapurit
|
||
|
ja solmun \key{vanhempi}
|
||
|
on sen ylemmän tason naapuri.
|
||
|
Jokaisella solmulla on tasan yksi vanhempi,
|
||
|
paitsi juurella ei ole vanhempaa.
|
||
|
Esimerkiksi yllä olevassa puussa solmun 4
|
||
|
lapset ovat solmut 3 ja 7 ja solmun 4 vanhempi on solmu 1.
|
||
|
|
||
|
\index{alipuu@alipuu}
|
||
|
|
||
|
Juurellisen puun rakenne on \emph{rekursiivinen}:
|
||
|
jokaisesta puun solmusta alkaa \key{alipuu},
|
||
|
jonka juurena on solmu itse ja johon kuuluvat
|
||
|
kaikki solmut, joihin solmusta pääsee kulkemalla alaspäin puussa.
|
||
|
Esimerkiksi solmun 4 alipuussa
|
||
|
ovat solmut 4, 3 ja 7.
|
||
|
|
||
|
\section{Puun läpikäynti}
|
||
|
|
||
|
Puun läpikäyntiin voi käyttää syvyyshakua ja
|
||
|
leveyshakua samaan
|
||
|
tapaan kuin yleisen verkon läpikäyntiin.
|
||
|
Erona on kuitenkin, että puussa ei ole silmukoita,
|
||
|
minkä ansiosta ei tarvitse huolehtia siitä,
|
||
|
että läpikäynti päätyisi tiettyyn
|
||
|
solmuun monesta eri suunnasta.
|
||
|
|
||
|
Tavallisin menetelmä puun läpikäyntiin on
|
||
|
valita tietty solmu juureksi ja aloittaa
|
||
|
siitä syvyyshaku.
|
||
|
Seuraava rekursiivinen funktio toteuttaa sen:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
void haku(int s, int e) {
|
||
|
// solmun s käsittely tähän
|
||
|
for (auto u : v[s]) {
|
||
|
if (u != e) haku(u, s);
|
||
|
}
|
||
|
}
|
||
|
\end{lstlisting}
|
||
|
|
||
|
Funktion parametrit ovat käsiteltävä solmu $s$
|
||
|
sekä edellinen käsitelty solmu $e$.
|
||
|
Parametrin $e$ ideana on varmistaa, että
|
||
|
läpikäynti etenee vain alaspäin puussa
|
||
|
sellaisiin solmuihin, joita ei ole vielä käsitelty.
|
||
|
|
||
|
Seuraava kutsu käy läpi puun aloittaen juuresta $x$:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
haku(x, 0);
|
||
|
\end{lstlisting}
|
||
|
|
||
|
Ensimmäisessä kutsussa $e=0$, koska läpikäynti
|
||
|
saa edetä juuresta kaikkiin suuntiin alaspäin.
|
||
|
|
||
|
\subsubsection{Dynaaminen ohjelmointi}
|
||
|
|
||
|
Puun läpikäyntiin voi myös yhdistää dynaamista
|
||
|
ohjelmointia ja laskea sen avulla jotakin tietoa puusta.
|
||
|
Dynaamisen ohjelmoinnin avulla voi esimerkiksi
|
||
|
laskea ajassa $O(n)$ jokaiselle solmulle,
|
||
|
montako solmua sen alipuussa
|
||
|
on tai kuinka pitkä on pisin solmusta
|
||
|
alaspäin jatkuva polku puussa.
|
||
|
|
||
|
Lasketaan esimerkiksi jokaiselle solmulle $s$
|
||
|
sen alipuun solmujen määrä $\texttt{c}[s]$.
|
||
|
Solmun alipuuhun kuuluvat solmu itse
|
||
|
sekä kaikki sen lasten alipuut.
|
||
|
Niinpä solmun alipuun solmujen määrä on
|
||
|
yhden suurempi kuin summa lasten
|
||
|
alipuiden solmujen määristä.
|
||
|
Laskennan voi toteuttaa seuraavasti:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
void haku(int s, int e) {
|
||
|
c[s] = 1;
|
||
|
for (auto u : v[s]) {
|
||
|
if (u == e) continue;
|
||
|
haku(u, s);
|
||
|
c[s] += c[u];
|
||
|
}
|
||
|
}
|
||
|
\end{lstlisting}
|
||
|
|
||
|
\section{Läpimitta}
|
||
|
|
||
|
\index{lzpimitta@läpimitta}
|
||
|
|
||
|
Puun \key{läpimitta} on pisin polku
|
||
|
kahden puussa olevan solmun välillä.
|
||
|
Esimerkiksi puussa
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (0,3) {$1$};
|
||
|
\node[draw, circle] (2) at (2,3) {$2$};
|
||
|
\node[draw, circle] (3) at (0,1) {$4$};
|
||
|
\node[draw, circle] (4) at (2,1) {$5$};
|
||
|
\node[draw, circle] (5) at (4,1) {$6$};
|
||
|
\node[draw, circle] (6) at (-2,3) {$7$};
|
||
|
\node[draw, circle] (7) at (-2,1) {$3$};
|
||
|
\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);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
läpimitta on 4, jota vastaa kaksi polkua:
|
||
|
solmujen 3 ja 6 välinen polku sekä
|
||
|
solmujen 7 ja 6 välinen polku.
|
||
|
|
||
|
Käymme seuraavaksi läpi kaksi tehokasta
|
||
|
algoritmia puun läpimitan laskeminen.
|
||
|
Molemmat algoritmit laskevat läpimitan ajassa
|
||
|
$O(n)$.
|
||
|
Ensimmäinen algoritmi perustuu dynaamiseen
|
||
|
ohjelmointiin, ja toinen algoritmi
|
||
|
laskee läpimitan kahden syvyyshaun avulla.
|
||
|
|
||
|
\subsubsection{Algoritmi 1}
|
||
|
|
||
|
Algoritmin alussa
|
||
|
yksi solmuista valitaan puun juureksi.
|
||
|
Tämän jälkeen algoritmi laskee
|
||
|
jokaiseen solmuun,
|
||
|
kuinka pitkä on pisin polku,
|
||
|
joka alkaa jostakin lehdestä,
|
||
|
nousee kyseiseen solmuun asti
|
||
|
ja laskeutuu toiseen lehteen.
|
||
|
Pisin tällainen polku vastaa puun läpimittaa.
|
||
|
|
||
|
Esimerkissä pisin polku alkaa lehdestä 7,
|
||
|
nousee solmuun 1 asti ja laskeutuu
|
||
|
sitten alas lehteen 6:
|
||
|
\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$};
|
||
|
\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,-,color=red,line width=2pt] (7) -- (3);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (3) -- (1);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (1) -- (2);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (2) -- (5);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Algoritmi laskee ensin dynaamisella ohjelmoinnilla
|
||
|
jokaiselle solmulle, kuinka pitkä on pisin polku,
|
||
|
joka lähtee solmusta alaspäin.
|
||
|
Esimerkiksi yllä olevassa puussa pisin polku
|
||
|
solmusta 1 alaspäin on pituudeltaan 2
|
||
|
(vaihtoehdot $1 \rightarrow 4 \rightarrow 3$,
|
||
|
$1 \rightarrow 4 \rightarrow 7$ ja $1 \rightarrow 2 \rightarrow 6$).
|
||
|
|
||
|
Tämän jälkeen algoritmi laskee kullekin solmulle,
|
||
|
kuinka pitkä on pisin polku, jossa solmu on käännekohtana.
|
||
|
Pisin tällainen polku syntyy valitsemalla kaksi lasta,
|
||
|
joista lähtee alaspäin mahdollisimman pitkä polku.
|
||
|
Esimerkiksi yllä olevassa puussa solmun 1 lapsista valitaan solmut 2 ja 4.
|
||
|
|
||
|
\subsubsection{Algoritmi 2}
|
||
|
|
||
|
Toinen tehokas tapa laskea puun läpimitta
|
||
|
perustuu kahteen syvyyshakuun.
|
||
|
Ensin valitaan mikä tahansa solmu $a$ puusta
|
||
|
ja etsitään siitä kaukaisin solmu $b$
|
||
|
syvyyshaulla.
|
||
|
Tämän jälkeen etsitään $b$:stä kaukaisin
|
||
|
solmu $c$ syvyyshaulla.
|
||
|
Puun läpimitta on etäisyys $b$:n ja $c$:n välillä.
|
||
|
|
||
|
Esimerkissä $a$, $b$ ja $c$ voisivat olla:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (0,3) {$1$};
|
||
|
\node[draw, circle] (2) at (2,3) {$2$};
|
||
|
\node[draw, circle] (3) at (0,1) {$4$};
|
||
|
\node[draw, circle] (4) at (2,1) {$5$};
|
||
|
\node[draw, circle] (5) at (4,1) {$6$};
|
||
|
\node[draw, circle] (6) at (-2,3) {$7$};
|
||
|
\node[draw, circle] (7) at (-2,1) {$3$};
|
||
|
\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);
|
||
|
\node[color=red] at (2,1.6) {$a$};
|
||
|
\node[color=red] at (-1.4,3) {$b$};
|
||
|
\node[color=red] at (4,1.6) {$c$};
|
||
|
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (6) -- (3);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (3) -- (1);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (1) -- (2);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (2) -- (5);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Menetelmä on tyylikäs, mutta miksi se toimii?
|
||
|
|
||
|
Tässä auttaa tarkastella puuta niin,
|
||
|
että puun läpimittaa vastaava polku on
|
||
|
levitetty vaakatasoon ja muut puun osat
|
||
|
riippuvat siitä alaspäin:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (2,1) {$1$};
|
||
|
\node[draw, circle] (2) at (4,1) {$2$};
|
||
|
\node[draw, circle] (3) at (0,1) {$4$};
|
||
|
\node[draw, circle] (4) at (2,-1) {$5$};
|
||
|
\node[draw, circle] (5) at (6,1) {$6$};
|
||
|
\node[draw, circle] (6) at (0,-1) {$3$};
|
||
|
\node[draw, circle] (7) at (-2,1) {$7$};
|
||
|
\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);
|
||
|
\node[color=red] at (2,-1.6) {$a$};
|
||
|
\node[color=red] at (-2,1.6) {$b$};
|
||
|
\node[color=red] at (6,1.6) {$c$};
|
||
|
\node[color=red] at (2,1.6) {$x$};
|
||
|
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (7) -- (3);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (3) -- (1);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (1) -- (2);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (2) -- (5);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Solmu $x$ on kohta,
|
||
|
jossa polku solmusta $a$ liittyy
|
||
|
läpimittaa vastaavaan polkuun.
|
||
|
Kaukaisin solmu $a$:sta
|
||
|
on solmu $b$, solmu $c$
|
||
|
tai jokin muu solmu, joka
|
||
|
on ainakin yhtä kaukana solmusta $x$.
|
||
|
Niinpä tämä solmu on aina sopiva
|
||
|
valinta läpimittaa vastaavan polun
|
||
|
toiseksi päätesolmuksi.
|
||
|
|
||
|
\section{Solmujen etäisyydet}
|
||
|
|
||
|
Vaikeampi tehtävä on laskea
|
||
|
jokaiselle puun solmulle
|
||
|
jokaiseen suuntaan, mikä on suurin
|
||
|
etäisyys johonkin kyseisessä suunnassa
|
||
|
olevaan solmuun.
|
||
|
Osoittautuu, että tämäkin tehtävä ratkeaa
|
||
|
ajassa $O(n)$ dynaamisella ohjelmoinnilla.
|
||
|
|
||
|
\begin{samepage}
|
||
|
Esimerkkipuussa etäisyydet ovat:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (0,3) {$1$};
|
||
|
\node[draw, circle] (2) at (2,3) {$2$};
|
||
|
\node[draw, circle] (3) at (0,1) {$4$};
|
||
|
\node[draw, circle] (4) at (2,1) {$5$};
|
||
|
\node[draw, circle] (5) at (4,1) {$6$};
|
||
|
\node[draw, circle] (6) at (-2,3) {$7$};
|
||
|
\node[draw, circle] (7) at (-2,1) {$3$};
|
||
|
\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);
|
||
|
\node[color=red] at (0.5,3.2) {$2$};
|
||
|
\node[color=red] at (0.3,2.4) {$1$};
|
||
|
\node[color=red] at (-0.2,2.4) {$2$};
|
||
|
\node[color=red] at (-0.2,1.5) {$3$};
|
||
|
\node[color=red] at (-0.5,1.2) {$1$};
|
||
|
\node[color=red] at (-1.7,2.4) {$4$};
|
||
|
\node[color=red] at (-0.5,0.8) {$1$};
|
||
|
\node[color=red] at (-1.5,0.8) {$4$};
|
||
|
\node[color=red] at (1.5,3.2) {$3$};
|
||
|
\node[color=red] at (1.5,1.2) {$3$};
|
||
|
\node[color=red] at (3.5,1.2) {$4$};
|
||
|
\node[color=red] at (2.2,2.4) {$1$};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
\end{samepage}
|
||
|
Esimerkiksi solmussa 4
|
||
|
kaukaisin solmu ylöspäin mentäessä
|
||
|
on solmu 6, johon etäisyys on 3 käyttäen
|
||
|
polkua $4 \rightarrow 1 \rightarrow 2 \rightarrow 6$.
|
||
|
|
||
|
\begin{samepage}
|
||
|
Tässäkin tehtävässä hyvä lähtökohta on
|
||
|
valita jokin solmu puun juureksi,
|
||
|
jolloin kaikki etäisyydet alaspäin
|
||
|
saa laskettua dynaamisella ohjelmoinnilla:
|
||
|
\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$};
|
||
|
\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);
|
||
|
|
||
|
\node[color=red] at (-2.5,0.7) {$1$};
|
||
|
\node[color=red] at (-1.5,0.7) {$1$};
|
||
|
|
||
|
\node[color=red] at (2.2,0.5) {$1$};
|
||
|
|
||
|
\node[color=red] at (-0.5,2.8) {$2$};
|
||
|
\node[color=red] at (0.2,2.5) {$1$};
|
||
|
\node[color=red] at (0.5,2.8) {$2$};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
\end{samepage}
|
||
|
|
||
|
Jäljelle jäävä tehtävä on laskea etäisyydet ylöspäin.
|
||
|
Tämä onnistuu tekemällä puuhun toinen läpikäynti,
|
||
|
joka pitää mukana tietoa,
|
||
|
mikä on suurin etäisyys solmun vanhemmasta
|
||
|
johonkin toisessa suunnassa olevaan solmuun.
|
||
|
|
||
|
Esimerkiksi solmun 2
|
||
|
suurin etäisyys ylöspäin on yhtä suurempi
|
||
|
kuin solmun 1 suurin etäisyys
|
||
|
johonkin muuhun suuntaan kuin solmuun 2:
|
||
|
\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$};
|
||
|
\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,-,color=red,line width=2pt] (1) -- (2);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (1) -- (3);
|
||
|
\path[draw,thick,-,color=red,line width=2pt] (3) -- (7);
|
||
|
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
Lopputuloksena on etäisyydet kaikista solmuista
|
||
|
kaikkiin suuntiin:
|
||
|
\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$};
|
||
|
\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);
|
||
|
|
||
|
\node[color=red] at (-2.5,0.7) {$1$};
|
||
|
\node[color=red] at (-1.5,0.7) {$1$};
|
||
|
|
||
|
\node[color=red] at (2.2,0.5) {$1$};
|
||
|
|
||
|
\node[color=red] at (-0.5,2.8) {$2$};
|
||
|
\node[color=red] at (0.2,2.5) {$1$};
|
||
|
\node[color=red] at (0.5,2.8) {$2$};
|
||
|
|
||
|
\node[color=red] at (-3,-0.4) {$4$};
|
||
|
\node[color=red] at (-1,-0.4) {$4$};
|
||
|
\node[color=red] at (-2,1.6) {$3$};
|
||
|
\node[color=red] at (2,1.6) {$3$};
|
||
|
|
||
|
\node[color=red] at (2.2,-0.4) {$4$};
|
||
|
\node[color=red] at (0.2,1.6) {$3$};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
%
|
||
|
% Kummankin läpikäynnin aikavaativuus on $O(n)$,
|
||
|
% joten algoritmin kokonais\-aikavaativuus on $O(n)$.
|
||
|
|
||
|
\section{Binääripuut}
|
||
|
|
||
|
\index{binxxripuu@binääripuu}
|
||
|
|
||
|
\begin{samepage}
|
||
|
\key{Binääripuu} on juurellinen puu,
|
||
|
jonka jokaisella solmulla on vasen ja oikea alipuu.
|
||
|
On mahdollista, että alipuu on tyhjä,
|
||
|
jolloin puu ei jatku siitä pidemmälle alaspäin.
|
||
|
Binääripuun jokaisella solmulla on 0, 1 tai 2 lasta.
|
||
|
|
||
|
Esimerkiksi seuraava puu on binääripuu:
|
||
|
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (0,0) {$1$};
|
||
|
\node[draw, circle] (2) at (-1.5,-1.5) {$2$};
|
||
|
\node[draw, circle] (3) at (1.5,-1.5) {$3$};
|
||
|
\node[draw, circle] (4) at (-3,-3) {$4$};
|
||
|
\node[draw, circle] (5) at (0,-3) {$5$};
|
||
|
\node[draw, circle] (6) at (-1.5,-4.5) {$6$};
|
||
|
\node[draw, circle] (7) at (3,-3) {$7$};
|
||
|
|
||
|
\path[draw,thick,-] (1) -- (2);
|
||
|
\path[draw,thick,-] (1) -- (3);
|
||
|
\path[draw,thick,-] (2) -- (4);
|
||
|
\path[draw,thick,-] (2) -- (5);
|
||
|
\path[draw,thick,-] (5) -- (6);
|
||
|
\path[draw,thick,-] (3) -- (7);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
\end{samepage}
|
||
|
|
||
|
Binääripuun solmuilla on kolme luontevaa järjestystä,
|
||
|
jotka syntyvät rekursiivisesta läpikäynnistä:
|
||
|
|
||
|
\index{esijxrjestys@esijärjestys}
|
||
|
\index{sisxjxrjestys@sisäjärjestys}
|
||
|
\index{jxlkijxrjestys@jälkijärjestys}
|
||
|
|
||
|
\begin{itemize}
|
||
|
\item \key{esijärjestys}: juuri, vasen alipuu, oikea alipuu
|
||
|
\item \key{sisäjärjestys}: vasen alipuu, juuri, oikea alipuu
|
||
|
\item \key{jälkijärjestys}: vasen alipuu, oikea alipuu, juuri
|
||
|
\end{itemize}
|
||
|
|
||
|
Esimerkissä kuvatun puun esijärjestys on
|
||
|
$[1,2,4,5,6,3,7]$,
|
||
|
sisäjärjestys on $[4,2,6,5,1,3,7]$
|
||
|
ja jälkijärjestys on $[4,6,5,2,7,3,1]$.
|
||
|
|
||
|
Osoittautuu, että tietämällä puun esijärjestyksen
|
||
|
ja sisäjärjestyksen voi päätellä puun koko rakenteen.
|
||
|
Esimerkiksi yllä oleva puu on ainoa mahdollinen
|
||
|
puu, jossa esijärjestys on
|
||
|
$[1,2,4,5,6,3,7]$ ja sisäjärjestys on $[4,2,6,5,1,3,7]$.
|
||
|
Vastaavasti myös jälkijärjestys ja sisäjärjestys
|
||
|
määrittävät puun rakenteen.
|
||
|
|
||
|
Tilanne on toinen, jos tiedossa on vain
|
||
|
esijärjestys ja jälkijärjestys.
|
||
|
Nämä järjestykset eivät
|
||
|
kuvaa välttämättä puuta yksikäsitteisesti.
|
||
|
Esimerkiksi molemmissa puissa
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\node[draw, circle] (1) at (0,0) {$1$};
|
||
|
\node[draw, circle] (2) at (-1.5,-1.5) {$2$};
|
||
|
\path[draw,thick,-] (1) -- (2);
|
||
|
|
||
|
\node[draw, circle] (1b) at (0+4,0) {$1$};
|
||
|
\node[draw, circle] (2b) at (1.5+4,-1.5) {$2$};
|
||
|
\path[draw,thick,-] (1b) -- (2b);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
esijärjestys on $(1,2)$ ja jälkijärjestys on $(2,1)$,
|
||
|
mutta siitä huolimatta puiden rakenteet eivät ole samat.
|
||
|
|