1176 lines
37 KiB
TeX
1176 lines
37 KiB
TeX
|
\chapter{Segment trees revisited}
|
||
|
|
||
|
\index{segmenttipuu@segmenttipuu}
|
||
|
|
||
|
Segmenttipuu on tehokas tietorakenne,
|
||
|
joka mahdollistaa monenlaisten
|
||
|
kyselyiden toteuttamisen tehokkaasti.
|
||
|
Tähän mennessä olemme käyttäneet
|
||
|
kuitenkin segmenttipuuta melko rajoittuneesti.
|
||
|
Nyt on aika tutustua pintaa syvemmältä
|
||
|
segmenttipuun mahdollisuuksiin.
|
||
|
|
||
|
Tähän mennessä olemme kulkeneet segmenttipuuta
|
||
|
\textit{alhaalta ylöspäin} lehdistä juureen.
|
||
|
Vaihtoehtoinen tapa toteuttaa puun käsittely
|
||
|
on kulkea \textit{ylhäältä alaspäin} juuresta lehtiin.
|
||
|
Tämä kulkusuunta on usein kätevä silloin,
|
||
|
kun kyseessä on perustilannetta
|
||
|
monimutkaisempi segmenttipuu.
|
||
|
|
||
|
Esimerkiksi välin $[a,b]$ summan laskeminen
|
||
|
segmenttipuussa tapahtuu alhaalta ylöspäin
|
||
|
tuttuun tapaan näin (luku 9.3):
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
int summa(int a, int b) {
|
||
|
a += N; b += N;
|
||
|
int s = 0;
|
||
|
while (a <= b) {
|
||
|
if (a%2 == 1) s += p[a++];
|
||
|
if (b%2 == 0) s += p[b--];
|
||
|
a /= 2; b /= 2;
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
\end{lstlisting}
|
||
|
Ylhäältä alaspäin toteutettuna funktiosta tulee:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
int summa(int a, int b, int k, int x, int y) {
|
||
|
if (b < x || a > y) return 0;
|
||
|
if (a == x && b == y) return p[k];
|
||
|
int d = (y-x+1)/2;
|
||
|
return summa(a, min(x+d-1,b), 2*k, x, x+d-1) +
|
||
|
summa(max(x+d,a), b, 2*k+1, x+d, y);
|
||
|
}
|
||
|
\end{lstlisting}
|
||
|
Nyt välin $[a,b]$ summan saa laskettua
|
||
|
kutsumalla funktiota näin:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
int s = summa(a, b, 1, 0, N-1);
|
||
|
\end{lstlisting}
|
||
|
|
||
|
Parametri $k$ ilmaisee kohdan
|
||
|
taulukossa \texttt{p}.
|
||
|
Aluksi $k$:n arvona on 1,
|
||
|
koska summan laskeminen alkaa
|
||
|
segmenttipuun juuresta.
|
||
|
|
||
|
Väli $[x,y]$ on parametria $k$ vastaava väli,
|
||
|
aluksi koko kyselyalue eli $[0,N-1]$.
|
||
|
Jos väli $[a,b]$ on välin $[x,y]$
|
||
|
ulkopuolella, välin summa on 0.
|
||
|
Jos taas välit $[a,b]$ ja $[x,y]$
|
||
|
ovat samat, summan saa taulukosta \texttt{p}.
|
||
|
|
||
|
Jos väli $[a,b]$ on kokonaan tai osittain välin $[x,y]$
|
||
|
sisällä, haku jatkuu rekursiivisesti
|
||
|
välin $[x,y]$ vasemmasta ja oikeasta puoliskosta.
|
||
|
Kummankin puoliskon koko on $d=\frac{1}{2}(y-x+1)$,
|
||
|
joten vasen puolisko kattaa välin $[x,x+d-1]$
|
||
|
ja oikea puolisko kattaa välin $[x+d,y]$.
|
||
|
|
||
|
Seuraava kuva näyttää,
|
||
|
kuinka haku etenee puussa,
|
||
|
kun lasketaan puun alle
|
||
|
merkityn välin summa.
|
||
|
Harmaat solmut ovat kohtia,
|
||
|
joissa rekursio päättyy ja välin
|
||
|
summan saa taulukosta \texttt{p}.
|
||
|
\\
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
\fill[color=gray!50] (5,0) rectangle (6,1);
|
||
|
\draw (0,0) grid (16,1);
|
||
|
|
||
|
\node[anchor=center] at (0.5, 0.5) {5};
|
||
|
\node[anchor=center] at (1.5, 0.5) {8};
|
||
|
\node[anchor=center] at (2.5, 0.5) {6};
|
||
|
\node[anchor=center] at (3.5, 0.5) {3};
|
||
|
\node[anchor=center] at (4.5, 0.5) {2};
|
||
|
\node[anchor=center] at (5.5, 0.5) {7};
|
||
|
\node[anchor=center] at (6.5, 0.5) {2};
|
||
|
\node[anchor=center] at (7.5, 0.5) {6};
|
||
|
\node[anchor=center] at (8.5, 0.5) {7};
|
||
|
\node[anchor=center] at (9.5, 0.5) {1};
|
||
|
\node[anchor=center] at (10.5, 0.5) {7};
|
||
|
\node[anchor=center] at (11.5, 0.5) {5};
|
||
|
\node[anchor=center] at (12.5, 0.5) {6};
|
||
|
\node[anchor=center] at (13.5, 0.5) {2};
|
||
|
\node[anchor=center] at (14.5, 0.5) {3};
|
||
|
\node[anchor=center] at (15.5, 0.5) {2};
|
||
|
|
||
|
%\node[anchor=center] at (1,2.5) {13};
|
||
|
|
||
|
\node[draw, circle] (a) at (1,2.5) {13};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,minimum size=22pt] (b) at (3,2.5) {9};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
\node[draw, circle,minimum size=22pt] (c) at (5,2.5) {9};
|
||
|
\path[draw,thick,-] (c) -- (4.5,1);
|
||
|
\path[draw,thick,-] (c) -- (5.5,1);
|
||
|
\node[draw, circle,fill=gray!50,minimum size=22pt] (d) at (7,2.5) {8};
|
||
|
\path[draw,thick,-] (d) -- (6.5,1);
|
||
|
\path[draw,thick,-] (d) -- (7.5,1);
|
||
|
\node[draw, circle,minimum size=22pt] (e) at (9,2.5) {8};
|
||
|
\path[draw,thick,-] (e) -- (8.5,1);
|
||
|
\path[draw,thick,-] (e) -- (9.5,1);
|
||
|
\node[draw, circle] (f) at (11,2.5) {12};
|
||
|
\path[draw,thick,-] (f) -- (10.5,1);
|
||
|
\path[draw,thick,-] (f) -- (11.5,1);
|
||
|
\node[draw, circle,fill=gray!50,minimum size=22pt] (g) at (13,2.5) {8};
|
||
|
\path[draw,thick,-] (g) -- (12.5,1);
|
||
|
\path[draw,thick,-] (g) -- (13.5,1);
|
||
|
\node[draw, circle,minimum size=22pt] (h) at (15,2.5) {5};
|
||
|
\path[draw,thick,-] (h) -- (14.5,1);
|
||
|
\path[draw,thick,-] (h) -- (15.5,1);
|
||
|
|
||
|
\node[draw, circle] (i) at (2,4.5) {22};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\node[draw, circle] (j) at (6,4.5) {17};
|
||
|
\path[draw,thick,-] (j) -- (c);
|
||
|
\path[draw,thick,-] (j) -- (d);
|
||
|
\node[draw, circle,fill=gray!50] (k) at (10,4.5) {20};
|
||
|
\path[draw,thick,-] (k) -- (e);
|
||
|
\path[draw,thick,-] (k) -- (f);
|
||
|
\node[draw, circle] (l) at (14,4.5) {13};
|
||
|
\path[draw,thick,-] (l) -- (g);
|
||
|
\path[draw,thick,-] (l) -- (h);
|
||
|
|
||
|
\node[draw, circle] (m) at (4,6.5) {39};
|
||
|
\path[draw,thick,-] (m) -- (i);
|
||
|
\path[draw,thick,-] (m) -- (j);
|
||
|
\node[draw, circle] (n) at (12,6.5) {33};
|
||
|
\path[draw,thick,-] (n) -- (k);
|
||
|
\path[draw,thick,-] (n) -- (l);
|
||
|
|
||
|
\node[draw, circle] (o) at (8,8.5) {72};
|
||
|
\path[draw,thick,-] (o) -- (m);
|
||
|
\path[draw,thick,-] (o) -- (n);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (o) -- (m);
|
||
|
\path[draw=red,thick,->,line width=2pt] (o) -- (n);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (m) -- (j);
|
||
|
\path[draw=red,thick,->,line width=2pt] (j) -- (c);
|
||
|
\path[draw=red,thick,->,line width=2pt] (j) -- (d);
|
||
|
\path[draw=red,thick,->,line width=2pt] (c) -- (5.5,1);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (n) -- (k);
|
||
|
\path[draw=red,thick,->,line width=2pt] (n) -- (l);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
||
|
|
||
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (5,-0.25);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
Myös tässä toteutuksessa kyselyn aikavaativuus on $O(\log n)$,
|
||
|
koska haun aikana käsiteltävien solmujen määrä on $O(\log n)$.
|
||
|
|
||
|
\section{Laiska eteneminen}
|
||
|
|
||
|
\index{laiska eteneminen@laiska eteneminen}
|
||
|
\index{laiska segmenttipuu@laiska segmenttipuu}
|
||
|
|
||
|
\key{Laiska eteneminen}
|
||
|
mahdollistaa segmenttipuun,
|
||
|
jossa voi sekä muuttaa väliä että kysyä tietoa väliltä
|
||
|
ajassa $O(\log n)$.
|
||
|
Ideana on suorittaa muutokset ja kyselyt ylhäältä
|
||
|
alaspäin ja toteuttaa muutokset laiskasti niin,
|
||
|
että ne välitetään puussa alaspäin vain silloin,
|
||
|
kun se on välttämätöntä.
|
||
|
|
||
|
Laiskassa segmenttipuussa solmuihin liittyy
|
||
|
kahdenlaista tietoa.
|
||
|
Kuten tavallisessa segmenttipuussa,
|
||
|
jokaisessa solmussa on sitä vastaavan välin
|
||
|
summa tai muu haluttu tieto.
|
||
|
Tämän lisäksi solmussa voi olla laiskaan etenemiseen
|
||
|
liittyvää tietoa, jota ei ole vielä välitetty
|
||
|
solmusta alaspäin.
|
||
|
|
||
|
Välin muutostapa voi olla joko
|
||
|
\textit{lisäys} tai \textit{asetus}.
|
||
|
Lisäyksessä välin jokaiseen alkioon lisätään
|
||
|
tietty arvo, ja asetuksessa välin
|
||
|
jokainen alkio saa tietyn arvon.
|
||
|
Kummankin operaation toteutus on melko samanlainen,
|
||
|
ja puu voi myös sallia samaan aikaan
|
||
|
molemmat muutostavat.
|
||
|
|
||
|
\subsubsection{Laiska segmenttipuu}
|
||
|
|
||
|
Tarkastellaan esimerkkinä tilannetta,
|
||
|
jossa segmenttipuun
|
||
|
tulee toteuttaa seuraavat operaatiot:
|
||
|
\begin{itemize}
|
||
|
\item lisää jokaisen välin $[a,b]$ alkioon arvo $u$
|
||
|
\item laske välin $[a,b]$ alkioiden summa
|
||
|
\end{itemize}
|
||
|
Toteutamme puun, jonka jokaisessa
|
||
|
solmussa on kaksi arvoa $s/z$:
|
||
|
välin lukujen summa $s$,
|
||
|
kuten tavallisessa segmenttipuussa, sekä
|
||
|
laiska muutos $z$,
|
||
|
joka tarkoittaa,
|
||
|
että kaikkiin välin lukuihin tulee lisätä $z$.
|
||
|
Seuraavassa puussa jokaisessa solmussa $z=0$
|
||
|
eli mitään muutoksia ei ole kesken.
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
\draw (0,0) grid (16,1);
|
||
|
|
||
|
\node[anchor=center] at (0.5, 0.5) {5};
|
||
|
\node[anchor=center] at (1.5, 0.5) {8};
|
||
|
\node[anchor=center] at (2.5, 0.5) {6};
|
||
|
\node[anchor=center] at (3.5, 0.5) {3};
|
||
|
\node[anchor=center] at (4.5, 0.5) {2};
|
||
|
\node[anchor=center] at (5.5, 0.5) {7};
|
||
|
\node[anchor=center] at (6.5, 0.5) {2};
|
||
|
\node[anchor=center] at (7.5, 0.5) {6};
|
||
|
\node[anchor=center] at (8.5, 0.5) {7};
|
||
|
\node[anchor=center] at (9.5, 0.5) {1};
|
||
|
\node[anchor=center] at (10.5, 0.5) {7};
|
||
|
\node[anchor=center] at (11.5, 0.5) {5};
|
||
|
\node[anchor=center] at (12.5, 0.5) {6};
|
||
|
\node[anchor=center] at (13.5, 0.5) {2};
|
||
|
\node[anchor=center] at (14.5, 0.5) {3};
|
||
|
\node[anchor=center] at (15.5, 0.5) {2};
|
||
|
|
||
|
\node[draw, circle] (a) at (1,2.5) {13/0};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (b) at (3,2.5) {9/0};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (c) at (5,2.5) {9/0};
|
||
|
\path[draw,thick,-] (c) -- (4.5,1);
|
||
|
\path[draw,thick,-] (c) -- (5.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (d) at (7,2.5) {8/0};
|
||
|
\path[draw,thick,-] (d) -- (6.5,1);
|
||
|
\path[draw,thick,-] (d) -- (7.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (e) at (9,2.5) {8/0};
|
||
|
\path[draw,thick,-] (e) -- (8.5,1);
|
||
|
\path[draw,thick,-] (e) -- (9.5,1);
|
||
|
\node[draw, circle] (f) at (11,2.5) {12/0};
|
||
|
\path[draw,thick,-] (f) -- (10.5,1);
|
||
|
\path[draw,thick,-] (f) -- (11.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (g) at (13,2.5) {8/0};
|
||
|
\path[draw,thick,-] (g) -- (12.5,1);
|
||
|
\path[draw,thick,-] (g) -- (13.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (h) at (15,2.5) {5/0};
|
||
|
\path[draw,thick,-] (h) -- (14.5,1);
|
||
|
\path[draw,thick,-] (h) -- (15.5,1);
|
||
|
|
||
|
\node[draw, circle] (i) at (2,4.5) {22/0};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\node[draw, circle] (j) at (6,4.5) {17/0};
|
||
|
\path[draw,thick,-] (j) -- (c);
|
||
|
\path[draw,thick,-] (j) -- (d);
|
||
|
\node[draw, circle] (k) at (10,4.5) {20/0};
|
||
|
\path[draw,thick,-] (k) -- (e);
|
||
|
\path[draw,thick,-] (k) -- (f);
|
||
|
\node[draw, circle] (l) at (14,4.5) {13/0};
|
||
|
\path[draw,thick,-] (l) -- (g);
|
||
|
\path[draw,thick,-] (l) -- (h);
|
||
|
|
||
|
\node[draw, circle] (m) at (4,6.5) {39/0};
|
||
|
\path[draw,thick,-] (m) -- (i);
|
||
|
\path[draw,thick,-] (m) -- (j);
|
||
|
\node[draw, circle] (n) at (12,6.5) {33/0};
|
||
|
\path[draw,thick,-] (n) -- (k);
|
||
|
\path[draw,thick,-] (n) -- (l);
|
||
|
|
||
|
\node[draw, circle] (o) at (8,8.5) {72/0};
|
||
|
\path[draw,thick,-] (o) -- (m);
|
||
|
\path[draw,thick,-] (o) -- (n);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Kun välin $[a,b]$ solmuja kasvatetaan $u$:lla,
|
||
|
alkaa kulku puun juuresta lehtiä kohti.
|
||
|
Kulun aikana tapahtuu kahdenlaisia muutoksia puun solmuihin:
|
||
|
|
||
|
Jos solmun väli $[x,y]$ kuuluu kokonaan
|
||
|
muutettavalle välille $[a,b]$,
|
||
|
solmun $z$-arvo kasvaa $u$:llä ja kulku pysähtyy.
|
||
|
Jos taas väli $[x,y]$ kuuluu osittain välille $[a,b]$,
|
||
|
solmun $s$-arvo kasvaa $hu$:llä,
|
||
|
missä $h$ on välien $[a,b]$ ja $[x,y]$ yhteisen osan pituus,
|
||
|
ja kulku jatkuu rekursiivisesti alaspäin.
|
||
|
|
||
|
Kasvatetaan esimerkiksi puun alle merkittyä väliä 2:lla:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
\fill[color=gray!50] (5,0) rectangle (6,1);
|
||
|
\draw (0,0) grid (16,1);
|
||
|
|
||
|
\node[anchor=center] at (0.5, 0.5) {5};
|
||
|
\node[anchor=center] at (1.5, 0.5) {8};
|
||
|
\node[anchor=center] at (2.5, 0.5) {6};
|
||
|
\node[anchor=center] at (3.5, 0.5) {3};
|
||
|
\node[anchor=center] at (4.5, 0.5) {2};
|
||
|
\node[anchor=center] at (5.5, 0.5) {9};
|
||
|
\node[anchor=center] at (6.5, 0.5) {2};
|
||
|
\node[anchor=center] at (7.5, 0.5) {6};
|
||
|
\node[anchor=center] at (8.5, 0.5) {7};
|
||
|
\node[anchor=center] at (9.5, 0.5) {1};
|
||
|
\node[anchor=center] at (10.5, 0.5) {7};
|
||
|
\node[anchor=center] at (11.5, 0.5) {5};
|
||
|
\node[anchor=center] at (12.5, 0.5) {6};
|
||
|
\node[anchor=center] at (13.5, 0.5) {2};
|
||
|
\node[anchor=center] at (14.5, 0.5) {3};
|
||
|
\node[anchor=center] at (15.5, 0.5) {2};
|
||
|
|
||
|
\node[draw, circle] (a) at (1,2.5) {13/0};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (b) at (3,2.5) {9/0};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (c) at (5,2.5) {11/0};
|
||
|
\path[draw,thick,-] (c) -- (4.5,1);
|
||
|
\path[draw,thick,-] (c) -- (5.5,1);
|
||
|
\node[draw, circle,fill=gray!50,minimum size=32pt] (d) at (7,2.5) {8/2};
|
||
|
\path[draw,thick,-] (d) -- (6.5,1);
|
||
|
\path[draw,thick,-] (d) -- (7.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (e) at (9,2.5) {8/0};
|
||
|
\path[draw,thick,-] (e) -- (8.5,1);
|
||
|
\path[draw,thick,-] (e) -- (9.5,1);
|
||
|
\node[draw, circle] (f) at (11,2.5) {12/0};
|
||
|
\path[draw,thick,-] (f) -- (10.5,1);
|
||
|
\path[draw,thick,-] (f) -- (11.5,1);
|
||
|
\node[draw, circle,fill=gray!50,minimum size=32pt] (g) at (13,2.5) {8/2};
|
||
|
\path[draw,thick,-] (g) -- (12.5,1);
|
||
|
\path[draw,thick,-] (g) -- (13.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (h) at (15,2.5) {5/0};
|
||
|
\path[draw,thick,-] (h) -- (14.5,1);
|
||
|
\path[draw,thick,-] (h) -- (15.5,1);
|
||
|
|
||
|
\node[draw, circle] (i) at (2,4.5) {22/0};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\node[draw, circle] (j) at (6,4.5) {23/0};
|
||
|
\path[draw,thick,-] (j) -- (c);
|
||
|
\path[draw,thick,-] (j) -- (d);
|
||
|
\node[draw, circle,fill=gray!50] (k) at (10,4.5) {20/2};
|
||
|
\path[draw,thick,-] (k) -- (e);
|
||
|
\path[draw,thick,-] (k) -- (f);
|
||
|
\node[draw, circle] (l) at (14,4.5) {17/0};
|
||
|
\path[draw,thick,-] (l) -- (g);
|
||
|
\path[draw,thick,-] (l) -- (h);
|
||
|
|
||
|
\node[draw, circle] (m) at (4,6.5) {45/0};
|
||
|
\path[draw,thick,-] (m) -- (i);
|
||
|
\path[draw,thick,-] (m) -- (j);
|
||
|
\node[draw, circle] (n) at (12,6.5) {45/0};
|
||
|
\path[draw,thick,-] (n) -- (k);
|
||
|
\path[draw,thick,-] (n) -- (l);
|
||
|
|
||
|
\node[draw, circle] (o) at (8,8.5) {90/0};
|
||
|
\path[draw,thick,-] (o) -- (m);
|
||
|
\path[draw,thick,-] (o) -- (n);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (o) -- (m);
|
||
|
\path[draw=red,thick,->,line width=2pt] (o) -- (n);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (m) -- (j);
|
||
|
\path[draw=red,thick,->,line width=2pt] (j) -- (c);
|
||
|
\path[draw=red,thick,->,line width=2pt] (j) -- (d);
|
||
|
\path[draw=red,thick,->,line width=2pt] (c) -- (5.5,1);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (n) -- (k);
|
||
|
\path[draw=red,thick,->,line width=2pt] (n) -- (l);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
||
|
|
||
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (5,-0.25);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Välin $[a,b]$ summan laskenta tapahtuu myös
|
||
|
kulkuna puun juuresta lehtiä kohti.
|
||
|
Jos solmun väli $[x,y]$ kuuluu kokonaan väliin $[a,b]$,
|
||
|
kyselyn summaan lisätään solmun $s$-arvo
|
||
|
sekä mahdollinen $z$-arvon tuottama lisäys.
|
||
|
Muussa tapauksessa kulku jatkuu rekursiivisesti alaspäin solmun lapsiin.
|
||
|
|
||
|
Aina ennen solmun käsittelyä siinä mahdollisesti
|
||
|
oleva laiska muutos välitetään tasoa alemmas.
|
||
|
Tämä tapahtuu sekä välin muutoskyselyssä
|
||
|
että summakyselyssä.
|
||
|
Ideana on, että laiska muutos etenee alaspäin
|
||
|
vain silloin, kun tämä on välttämätöntä,
|
||
|
jotta puun käsittely on tehokasta.
|
||
|
|
||
|
Seuraava kuva näyttää, kuinka äskeinen puu muuttuu,
|
||
|
kun siitä lasketaan puun alle merkityn välin summa:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
\draw (0,0) grid (16,1);
|
||
|
|
||
|
\node[anchor=center] at (0.5, 0.5) {5};
|
||
|
\node[anchor=center] at (1.5, 0.5) {8};
|
||
|
\node[anchor=center] at (2.5, 0.5) {6};
|
||
|
\node[anchor=center] at (3.5, 0.5) {3};
|
||
|
\node[anchor=center] at (4.5, 0.5) {2};
|
||
|
\node[anchor=center] at (5.5, 0.5) {9};
|
||
|
\node[anchor=center] at (6.5, 0.5) {2};
|
||
|
\node[anchor=center] at (7.5, 0.5) {6};
|
||
|
\node[anchor=center] at (8.5, 0.5) {7};
|
||
|
\node[anchor=center] at (9.5, 0.5) {1};
|
||
|
\node[anchor=center] at (10.5, 0.5) {7};
|
||
|
\node[anchor=center] at (11.5, 0.5) {5};
|
||
|
\node[anchor=center] at (12.5, 0.5) {6};
|
||
|
\node[anchor=center] at (13.5, 0.5) {2};
|
||
|
\node[anchor=center] at (14.5, 0.5) {3};
|
||
|
\node[anchor=center] at (15.5, 0.5) {2};
|
||
|
|
||
|
\node[draw, circle] (a) at (1,2.5) {13/0};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (b) at (3,2.5) {9/0};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (c) at (5,2.5) {11/0};
|
||
|
\path[draw,thick,-] (c) -- (4.5,1);
|
||
|
\path[draw,thick,-] (c) -- (5.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (d) at (7,2.5) {8/2};
|
||
|
\path[draw,thick,-] (d) -- (6.5,1);
|
||
|
\path[draw,thick,-] (d) -- (7.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (e) at (9,2.5) {8/2};
|
||
|
\path[draw,thick,-] (e) -- (8.5,1);
|
||
|
\path[draw,thick,-] (e) -- (9.5,1);
|
||
|
\node[draw, circle,fill=gray!50,] (f) at (11,2.5) {12/2};
|
||
|
\path[draw,thick,-] (f) -- (10.5,1);
|
||
|
\path[draw,thick,-] (f) -- (11.5,1);
|
||
|
\node[draw, circle,fill=gray!50,minimum size=32pt] (g) at (13,2.5) {8/2};
|
||
|
\path[draw,thick,-] (g) -- (12.5,1);
|
||
|
\path[draw,thick,-] (g) -- (13.5,1);
|
||
|
\node[draw, circle,minimum size=32pt] (h) at (15,2.5) {5/0};
|
||
|
\path[draw,thick,-] (h) -- (14.5,1);
|
||
|
\path[draw,thick,-] (h) -- (15.5,1);
|
||
|
|
||
|
\node[draw, circle] (i) at (2,4.5) {22/0};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\node[draw, circle] (j) at (6,4.5) {23/0};
|
||
|
\path[draw,thick,-] (j) -- (c);
|
||
|
\path[draw,thick,-] (j) -- (d);
|
||
|
\node[draw, circle] (k) at (10,4.5) {28/0};
|
||
|
\path[draw,thick,-] (k) -- (e);
|
||
|
\path[draw,thick,-] (k) -- (f);
|
||
|
\node[draw, circle] (l) at (14,4.5) {17/0};
|
||
|
\path[draw,thick,-] (l) -- (g);
|
||
|
\path[draw,thick,-] (l) -- (h);
|
||
|
|
||
|
\node[draw, circle] (m) at (4,6.5) {45/0};
|
||
|
\path[draw,thick,-] (m) -- (i);
|
||
|
\path[draw,thick,-] (m) -- (j);
|
||
|
\node[draw, circle] (n) at (12,6.5) {45/0};
|
||
|
\path[draw,thick,-] (n) -- (k);
|
||
|
\path[draw,thick,-] (n) -- (l);
|
||
|
|
||
|
\node[draw, circle] (o) at (8,8.5) {90/0};
|
||
|
\path[draw,thick,-] (o) -- (m);
|
||
|
\path[draw,thick,-] (o) -- (n);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (o) -- (n);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (n) -- (k);
|
||
|
\path[draw=red,thick,->,line width=2pt] (n) -- (l);
|
||
|
|
||
|
\path[draw=red,thick,->,line width=2pt] (k) -- (f);
|
||
|
\path[draw=red,thick,->,line width=2pt] (l) -- (g);
|
||
|
|
||
|
\draw [decoration={brace}, decorate, line width=0.5mm] (14,-0.25) -- (10,-0.25);
|
||
|
|
||
|
\draw[color=blue,thick] (8,1.5) rectangle (12,5.5);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
Tämän kyselyn seurauksena laiska muutos eteni alaspäin
|
||
|
laatikolla ympäröidyssä puun osassa.
|
||
|
Laiskaa muutosta täytyi viedä alaspäin, koska kyselyn
|
||
|
kohteena oleva väli osui osittain laiskan muutoksen välille.
|
||
|
|
||
|
Huomaa, että joskus puussa olevia laiskoja muutoksia täytyy yhdistää.
|
||
|
Näin tapahtuu silloin, kun solmussa on valmiina laiska muutos
|
||
|
ja siihen tulee ylhäältä toinen laiska muutos.
|
||
|
Tässä tapauksessa yhdistäminen on helppoa,
|
||
|
koska muutokset $z_1$ ja $z_2$ aiheuttavat yhdessä muutoksen $z_1+z_2$.
|
||
|
|
||
|
\subsubsection{Polynomimuutos}
|
||
|
|
||
|
Laiskaa segmenttipuuta voi yleistää niin,
|
||
|
että väliä muuttaa polynomi
|
||
|
\[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\]
|
||
|
|
||
|
Ideana on, että välin ensimmäisen kohdan
|
||
|
muutos on $p(0)$, toisen kohdan muutos on $p(1)$ jne.,
|
||
|
eli välin $[a,b]$ kohdan $i$ muutos on $p(i-a)$.
|
||
|
Esimerkiksi polynomin $p(u)=u+1$ lisäys välille
|
||
|
$[a,b]$ tarkoittaa, että kohta $a$ kasvaa 1:llä,
|
||
|
kohta $a+1$ kasvaa 2:lla, kohta $a+2$ kasvaa 3:lla jne.
|
||
|
|
||
|
Polynomimuutoksen voi toteuttaa niin,
|
||
|
että jokaisessa solmussa on $k+2$ arvoa,
|
||
|
missä $k$ on polynomin asteluku.
|
||
|
Arvo $s$ kertoo solmua vastaavan välin summan kuten ennenkin,
|
||
|
ja arvot $z_0,z_1,\ldots,z_k$ ovat polynomin kertoimet,
|
||
|
joka ilmaisee väliin kohdistuvan laiskan muutoksen.
|
||
|
|
||
|
Nyt välin $[x,y]$ summa on
|
||
|
\[s+\sum_{u=0}^{y-x} z_k u^k + z_{k-1} u^{k-1} + \cdots + z_0,\]
|
||
|
jonka saa laskettua tehokkaasti osissa summakaavoilla.
|
||
|
Esimerkiksi termin $z_0$ summaksi tulee
|
||
|
$(y-x+1)z_0$ ja termin $z_1 u$ summaksi tulee
|
||
|
\[z_1(0+1+\cdots+y-x) = z_1 \frac{(y-x)(y-x+1)}{2} .\]
|
||
|
|
||
|
Kun muutos etenee alaspäin puussa,
|
||
|
polynomin $p(u)$ indeksointi muuttuu,
|
||
|
koska jokaisella välillä $[x,y]$
|
||
|
polynomin arvot tulevat kohdista $x=0,1,\ldots,y-x$.
|
||
|
Tämä ei kuitenkaan tuota ongelmia,
|
||
|
koska $p'(u)=p(u+h)$ on aina
|
||
|
samanasteinen polynomi kuin $p(u)$.
|
||
|
Esimerkiksi jos $p(u)=t_2 u^2+t_1 u-t_0$, niin
|
||
|
\[p'(u)=t_2(u+h)^2+t_1(u+h)-t_0=t_2 u^2 + (2ht_2+t_1)u+t_2h^2+t_1h-t_0.\]
|
||
|
|
||
|
\section{Dynaaminen toteutus}
|
||
|
|
||
|
\index{dynaaminen segmenttipuu@dynaaminen segmenttipuu}
|
||
|
|
||
|
Tavallinen segmenttipuu on staattinen,
|
||
|
eli jokaiselle solmulle on paikka taulukossa
|
||
|
ja puu vie kiinteän määrän muistia.
|
||
|
Tämä toteutus kuitenkin tuhlaa muistia,
|
||
|
jos suurin osa puun solmuista on tyhjiä.
|
||
|
\key{Dynaaminen segmenttipuu} varaa muistia vain
|
||
|
niille solmuille, joita todella tarvitaan.
|
||
|
|
||
|
Solmut on kätevää tallentaa tietueina tähän tapaan:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
struct node {
|
||
|
int s;
|
||
|
int x, y;
|
||
|
node *l, *r;
|
||
|
node(int x, int a, int b) : s(s), x(x), y(y) {}
|
||
|
};
|
||
|
\end{lstlisting}
|
||
|
Tässä $s$ on solmussa oleva arvo,
|
||
|
$[x,y]$ on solmua vastaava väli
|
||
|
ja $l$ ja $r$ osoittavat
|
||
|
solmun vasempaan ja oikeaan alipuuhun.
|
||
|
|
||
|
Tämän jälkeen solmuja voi käsitellä seuraavasti:
|
||
|
|
||
|
\begin{lstlisting}
|
||
|
// uuden solmun luonti
|
||
|
node *u = new node(0, 0, 15);
|
||
|
// kentän muuttaminen
|
||
|
u->s = 5;
|
||
|
\end{lstlisting}
|
||
|
|
||
|
\subsubsection{Harva segmenttipuu}
|
||
|
|
||
|
\index{harva segmenttipuu@harva segmenttipuu}
|
||
|
|
||
|
Dynaaminen segmenttipuu on hyödyllinen,
|
||
|
jos puun indeksialue $[0,N-1]$ on \textit{harva}
|
||
|
eli $N$ on suuri mutta vain
|
||
|
pieni osa indekseistä on käytössä.
|
||
|
Siinä missä tavallinen segmenttipuu
|
||
|
vie muistia $O(N)$,
|
||
|
dynaaminen segmenttipuu vie muistia
|
||
|
vain $O(n \log N)$, missä $n$ on
|
||
|
käytössä olevien indeksien määrä.
|
||
|
|
||
|
\key{Harva segmenttipuu} aluksi tyhjä
|
||
|
ja sen ainoa solmu on $[0,N-1]$.
|
||
|
Kun puu muuttuu, siihen lisätään
|
||
|
solmuja dynaamisesti sitä mukaa kuin niitä tarvitaan
|
||
|
uusien indeksien vuoksi.
|
||
|
Esimerkiksi jos $N=16$ ja indeksejä
|
||
|
3 ja 10 on muutettu,
|
||
|
puu sisältää seuraavat solmut:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.9]
|
||
|
\scriptsize
|
||
|
\node[draw, circle,minimum size=35pt] (1) at (0,0) {$[0,15]$};
|
||
|
\node[draw, circle,minimum size=35pt] (2) at (-4,-2) {$[0,7]$};
|
||
|
\node[draw, circle,minimum size=35pt] (3) at (-6,-4) {$[0,3]$};
|
||
|
\node[draw, circle,minimum size=35pt] (4) at (-4,-6) {$[2,3]$};
|
||
|
\node[draw, circle,minimum size=35pt] (5) at (-2,-8) {$[3]$};
|
||
|
\node[draw, circle,minimum size=35pt] (6) at (4,-2) {$[8,15]$};
|
||
|
\node[draw, circle,minimum size=35pt] (7) at (2,-4) {$[8,11]$};
|
||
|
\node[draw, circle,minimum size=35pt] (8) at (4,-6) {$[10,11]$};
|
||
|
\node[draw, circle,minimum size=35pt] (9) at (2,-8) {$[10]$};
|
||
|
|
||
|
\path[draw,thick,->] (1) -- (2);
|
||
|
\path[draw,thick,->] (2) -- (3);
|
||
|
\path[draw,thick,->] (3) -- (4);
|
||
|
\path[draw,thick,->] (4) -- (5);
|
||
|
|
||
|
\path[draw,thick,->] (1) -- (6);
|
||
|
\path[draw,thick,->] (6) -- (7);
|
||
|
\path[draw,thick,->] (7) -- (8);
|
||
|
\path[draw,thick,->] (8) -- (9);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Reitti puun juuresta lehteen sisältää
|
||
|
$O(\log N)$ solmua,
|
||
|
joten jokainen muutos puuhun lisää
|
||
|
enintään $O(\log N)$ uutta solmua puuhun.
|
||
|
Niinpä $n$ muutoksen jälkeen puussa
|
||
|
on enintään $O(n \log N)$ solmua.
|
||
|
|
||
|
Huomaa, että jos kaikki tarvittavat indeksit
|
||
|
ovat tiedossa
|
||
|
algoritmin alussa, dynaamisen segmenttipuun
|
||
|
sijasta voi käyttää tavallista segmenttipuuta
|
||
|
ja indeksien pakkausta (luku 9.4).
|
||
|
Tämä ei ole kuitenkaan mahdollista,
|
||
|
jos indeksit syntyvät vasta algoritmin aikana.
|
||
|
|
||
|
\subsubsection{Persistentti segmenttipuu}
|
||
|
|
||
|
\index{persistentti segmenttipuu@persistentti segmenttipuu}
|
||
|
\index{muutoshistoria@muutoshistoria}
|
||
|
|
||
|
Dynaamisen toteutuksen avulla on myös
|
||
|
mahdollista luoda \key{persistentti segmenttipuu},
|
||
|
joka säilyttää puun muutoshistorian.
|
||
|
Tällöin muistissa on jokainen
|
||
|
segmenttipuun vaihe, joka on esiintynyt
|
||
|
algoritmin suorituksen aikana.
|
||
|
|
||
|
Muutoshistorian hyötynä on,
|
||
|
että kaikkia vanhoja puita voi käsitellä
|
||
|
segmenttipuun tapaan,
|
||
|
koska niiden rakenne on edelleen olemassa.
|
||
|
Vanhoista puista voi myös johtaa uusia
|
||
|
puita, joita voi muokata edelleen.
|
||
|
|
||
|
Tarkastellaan esimerkiksi seuraavaa muutossarjaa,
|
||
|
jossa punaiset solmut muuttuvat päivityksessä
|
||
|
ja muut solmut säilyvät ennallaan:
|
||
|
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.8]
|
||
|
\node[draw, circle,minimum size=13pt] (1a) at (3,0) {};
|
||
|
\node[draw, circle,minimum size=13pt] (2a) at (2,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (3a) at (4,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (4a) at (1.5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (5a) at (2.5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (6a) at (3.5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (7a) at (4.5,-2) {};
|
||
|
\path[draw,thick,->] (1a) -- (2a);
|
||
|
\path[draw,thick,->] (1a) -- (3a);
|
||
|
\path[draw,thick,->] (2a) -- (4a);
|
||
|
\path[draw,thick,->] (2a) -- (5a);
|
||
|
\path[draw,thick,->] (3a) -- (6a);
|
||
|
\path[draw,thick,->] (3a) -- (7a);
|
||
|
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (1b) at (3+5,0) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (2b) at (2+5,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (3b) at (4+5,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (4b) at (1.5+5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (5b) at (2.5+5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (6b) at (3.5+5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (7b) at (4.5+5,-2) {};
|
||
|
\path[draw,thick,->] (1b) -- (2b);
|
||
|
\path[draw,thick,->] (1b) -- (3b);
|
||
|
\path[draw,thick,->] (2b) -- (4b);
|
||
|
\path[draw,thick,->] (2b) -- (5b);
|
||
|
\path[draw,thick,->] (3b) -- (6b);
|
||
|
\path[draw,thick,->] (3b) -- (7b);
|
||
|
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (1c) at (3+10,0) {};
|
||
|
\node[draw, circle,minimum size=13pt] (2c) at (2+10,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (3c) at (4+10,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (4c) at (1.5+10,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (5c) at (2.5+10,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (6c) at (3.5+10,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (7c) at (4.5+10,-2) {};
|
||
|
\path[draw,thick,->] (1c) -- (2c);
|
||
|
\path[draw,thick,->] (1c) -- (3c);
|
||
|
\path[draw,thick,->] (2c) -- (4c);
|
||
|
\path[draw,thick,->] (2c) -- (5c);
|
||
|
\path[draw,thick,->] (3c) -- (6c);
|
||
|
\path[draw,thick,->] (3c) -- (7c);
|
||
|
|
||
|
\node at (3,-3) {vaihe 1};
|
||
|
\node at (3+5,-3) {vaihe 2};
|
||
|
\node at (3+10,-3) {vaihe 3};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
Jokaisen muutoksen jälkeen suurin osa puun
|
||
|
solmuista säilyy ennallaan,
|
||
|
joten muistia säästävä tapa tallentaa muutoshistoria
|
||
|
on käyttää mahdollisimman paljon hyväksi
|
||
|
puun vanhoja osia muutoksissa.
|
||
|
Tässä tapauksessa muutoshistorian voi
|
||
|
tallentaa seuraavasti:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.8]
|
||
|
\path[use as bounding box] (0, 1) rectangle (16, -3.5);
|
||
|
|
||
|
\node[draw, circle,minimum size=13pt] (1a) at (3,0) {};
|
||
|
\node[draw, circle,minimum size=13pt] (2a) at (2,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (3a) at (4,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt] (4a) at (1.5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (5a) at (2.5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (6a) at (3.5,-2) {};
|
||
|
\node[draw, circle,minimum size=13pt] (7a) at (4.5,-2) {};
|
||
|
\path[draw,thick,->] (1a) -- (2a);
|
||
|
\path[draw,thick,->] (1a) -- (3a);
|
||
|
\path[draw,thick,->] (2a) -- (4a);
|
||
|
\path[draw,thick,->] (2a) -- (5a);
|
||
|
\path[draw,thick,->] (3a) -- (6a);
|
||
|
\path[draw,thick,->] (3a) -- (7a);
|
||
|
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (1b) at (3+5,0) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (2b) at (2+5,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (5b) at (2.5+5,-2) {};
|
||
|
\path[draw,thick,->] (1b) -- (2b);
|
||
|
|
||
|
\draw[thick,->] (1b) .. controls (3+5+2,0-1) and (3+5,2.5) .. (3a);
|
||
|
|
||
|
\draw[thick,->] (2b) .. controls (2+5-0.5,-1-0.5) and (2,4.5) .. (4a);
|
||
|
|
||
|
|
||
|
\path[draw,thick,->] (2b) -- (5b);
|
||
|
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (1c) at (3+10,0) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (3c) at (4+10,-1) {};
|
||
|
\node[draw, circle,minimum size=13pt,fill=red] (7c) at (4.5+10,-2) {};
|
||
|
\path[draw,thick,->] (1c) -- (2b);
|
||
|
\path[draw,thick,->] (1c) -- (3c);
|
||
|
|
||
|
\draw[thick,->] (3c) .. controls (2.5+5,-3) and (3.5,-3) .. (6a);
|
||
|
|
||
|
\path[draw,thick,->] (3c) -- (7c);
|
||
|
|
||
|
\node at (3,-3) {vaihe 1};
|
||
|
\node at (3+5,-3) {vaihe 2};
|
||
|
\node at (3+10,-3) {vaihe 3};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Nyt muistissa on jokaisesta puun vaiheesta
|
||
|
puun juuri, jonka avulla pystyy selvittämään
|
||
|
koko puun rakenteen kyseisellä hetkellä.
|
||
|
Jokainen muutos tuo vain $O(\log N)$ uutta solmua puuhun,
|
||
|
kun puun indeksialue on $[0,N-1]$,
|
||
|
joten koko muutoshistorian pitäminen muistissa on mahdollista.
|
||
|
|
||
|
\section{Tietorakenteet}
|
||
|
|
||
|
Segmenttipuun solmussa voi olla
|
||
|
yksittäisen arvon
|
||
|
sijasta myös jokin tietorakenne,
|
||
|
joka pitää yllä tietoa solmua vastaavasta välistä.
|
||
|
Tällöin segmenttipuun operaatiot vievät aikaa
|
||
|
$O(f(n) \log n)$, missä $f(n)$ on
|
||
|
yksittäisen solmun tietorakenteen
|
||
|
käsittelyyn kuluva aika.
|
||
|
|
||
|
Tarkastellaan esimerkkinä segmenttipuuta,
|
||
|
jonka avulla voi laskea, montako kertaa
|
||
|
luku $x$ esiintyy taulukon välillä $[a,b]$.
|
||
|
Esimerkiksi seuraavassa taulukossa
|
||
|
luku 1 esiintyy kolme kertaa
|
||
|
merkityllä välillä:
|
||
|
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
\fill[lightgray] (1,0) rectangle (6,1);
|
||
|
\draw (0,0) grid (8,1);
|
||
|
|
||
|
\node[anchor=center] at (0.5, 0.5) {3};
|
||
|
\node[anchor=center] at (1.5, 0.5) {1};
|
||
|
\node[anchor=center] at (2.5, 0.5) {2};
|
||
|
\node[anchor=center] at (3.5, 0.5) {3};
|
||
|
\node[anchor=center] at (4.5, 0.5) {1};
|
||
|
\node[anchor=center] at (5.5, 0.5) {1};
|
||
|
\node[anchor=center] at (6.5, 0.5) {1};
|
||
|
\node[anchor=center] at (7.5, 0.5) {2};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Ideana on toteuttaa segmenttipuu, jonka
|
||
|
jokaisessa solmussa on tietorakenne,
|
||
|
josta voi kysyä,
|
||
|
montako kertaa luku $x$ esiintyy solmun välillä.
|
||
|
Tällaisen segmenttipuun avulla
|
||
|
vastaus kyselyyn syntyy laskemalla yhteen
|
||
|
esiintymismäärät väleiltä, joista $[a,b]$ muodostuu.
|
||
|
|
||
|
Esimerkiksi yllä olevasta taulukosta syntyy
|
||
|
seuraava segmenttipuu:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
|
||
|
\node[draw, rectangle] (a) at (1,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
3 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (b) at (3,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
1 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (c) at (5,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
2 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (d) at (7,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
3 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (e) at (9,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
1 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (f) at (11,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
1 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (g) at (13,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
1 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
\node[draw, rectangle] (h) at (15,2.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
2 \\
|
||
|
\hline
|
||
|
1 \\
|
||
|
\end{tabular}};
|
||
|
|
||
|
\node[draw, rectangle] (i) at (2,4.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{rr}
|
||
|
1 & 3 \\
|
||
|
\hline
|
||
|
1 & 1 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\node[draw, rectangle] (j) at (6,4.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{rr}
|
||
|
2 & 3 \\
|
||
|
\hline
|
||
|
1 & 1 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (j) -- (c);
|
||
|
\path[draw,thick,-] (j) -- (d);
|
||
|
\node[draw, rectangle] (k) at (10,4.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{r}
|
||
|
1 \\
|
||
|
\hline
|
||
|
2 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (k) -- (e);
|
||
|
\path[draw,thick,-] (k) -- (f);
|
||
|
\node[draw, rectangle] (l) at (14,4.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{rr}
|
||
|
1 & 2 \\
|
||
|
\hline
|
||
|
1 & 1 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (l) -- (g);
|
||
|
\path[draw,thick,-] (l) -- (h);
|
||
|
|
||
|
\node[draw, rectangle] (m) at (4,6.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{rrr}
|
||
|
1 & 2 & 3 \\
|
||
|
\hline
|
||
|
1 & 1 & 2 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (m) -- (i);
|
||
|
\path[draw,thick,-] (m) -- (j);
|
||
|
\node[draw, rectangle] (n) at (12,6.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{rr}
|
||
|
1 & 2 \\
|
||
|
\hline
|
||
|
3 & 1 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (n) -- (k);
|
||
|
\path[draw,thick,-] (n) -- (l);
|
||
|
|
||
|
\node[draw, rectangle] (o) at (8,8.5)
|
||
|
{
|
||
|
\footnotesize
|
||
|
\begin{tabular}{rrr}
|
||
|
1 & 2 & 3 \\
|
||
|
\hline
|
||
|
4 & 2 & 2 \\
|
||
|
\end{tabular}};
|
||
|
\path[draw,thick,-] (o) -- (m);
|
||
|
\path[draw,thick,-] (o) -- (n);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
|
||
|
Sopiva tietorakenne segmenttipuun toteuttamiseen on
|
||
|
hakemistorakenne, joka pitää kirjaa välillä esiintyvien
|
||
|
lukujen määrästä.
|
||
|
Esimerkiksi \texttt{map}-ra\-ken\-net\-ta käyttäen
|
||
|
yhden solmun käsittely vie aikaa $O(\log n)$,
|
||
|
minkä seurauksena kyselyn aikavaativuus on $O(\log^2 n)$.
|
||
|
|
||
|
Solmuissa olevat tietorakenteet kasvattavat
|
||
|
segmenttipuun muistinkäyttöä.
|
||
|
Tässä tapauksessa
|
||
|
segmenttipuu vie tilaa $O(n \log n)$,
|
||
|
koska siinä on $O(\log n)$ tasoa, joista
|
||
|
jokaisella hakemistorakenteet sisältävät $O(n)$ lukua.
|
||
|
|
||
|
\section{Kaksiulotteisuus}
|
||
|
|
||
|
\index{kaksiulotteinen segmenttipuu@kaksiulotteinen segmenttipuu}
|
||
|
|
||
|
\key{Kaksiulotteinen segmenttipuu} mahdollistaa
|
||
|
kaksiulotteisen taulukon
|
||
|
suorakulmaisia alueita koskevat kyselyt.
|
||
|
Tällaisen segmenttipuun voi toteuttaa
|
||
|
sisäkkäisinä segmenttipuina:
|
||
|
suuri puu vastaa taulukon rivejä
|
||
|
ja sen kussakin solmussa on pieni puu,
|
||
|
joka vastaa taulukon sarakkeita.
|
||
|
|
||
|
Esimerkiksi taulukon
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.7]
|
||
|
\draw (0,0) grid (4,4);
|
||
|
|
||
|
\node[anchor=center] at (0.5, 0.5) {8};
|
||
|
\node[anchor=center] at (1.5, 0.5) {5};
|
||
|
\node[anchor=center] at (2.5, 0.5) {3};
|
||
|
\node[anchor=center] at (3.5, 0.5) {8};
|
||
|
|
||
|
\node[anchor=center] at (0.5, 1.5) {3};
|
||
|
\node[anchor=center] at (1.5, 1.5) {9};
|
||
|
\node[anchor=center] at (2.5, 1.5) {7};
|
||
|
\node[anchor=center] at (3.5, 1.5) {1};
|
||
|
|
||
|
\node[anchor=center] at (0.5, 2.5) {8};
|
||
|
\node[anchor=center] at (1.5, 2.5) {7};
|
||
|
\node[anchor=center] at (2.5, 2.5) {5};
|
||
|
\node[anchor=center] at (3.5, 2.5) {2};
|
||
|
|
||
|
\node[anchor=center] at (0.5, 3.5) {7};
|
||
|
\node[anchor=center] at (1.5, 3.5) {6};
|
||
|
\node[anchor=center] at (2.5, 3.5) {1};
|
||
|
\node[anchor=center] at (3.5, 3.5) {6};
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
alueiden summia voi laskea seuraavasta segmenttipuusta:
|
||
|
\begin{center}
|
||
|
\begin{tikzpicture}[scale=0.4]
|
||
|
\footnotesize
|
||
|
\begin{scope}[shift={(-12,0)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {7};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {6};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {1};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {6};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {13};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=2.5pt] (b) at (3,2.5) {7};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {20};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\begin{scope}[shift={(-4,0)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {8};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {7};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {5};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {2};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {15};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=2.5pt] (b) at (3,2.5) {7};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {22};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\begin{scope}[shift={(4,0)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {3};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {9};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {7};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {1};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {12};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=2.5pt] (b) at (3,2.5) {8};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {20};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\begin{scope}[shift={(12,0)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {8};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {5};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {3};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {8};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {13};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (b) at (3,2.5) {11};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {24};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\begin{scope}[shift={(-8,10)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {15};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {13};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {6};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {8};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {28};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (b) at (3,2.5) {14};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {42};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\begin{scope}[shift={(8,10)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {11};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {14};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {10};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {9};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {25};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (b) at (3,2.5) {19};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {44};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\begin{scope}[shift={(0,20)}]
|
||
|
\draw (-1,-1) rectangle (5,6);
|
||
|
\draw (0,0) grid (4,1);
|
||
|
\node[anchor=center,scale=0.8] at (0.5, 0.5) {26};
|
||
|
\node[anchor=center,scale=0.8] at (1.5, 0.5) {27};
|
||
|
\node[anchor=center,scale=0.8] at (2.5, 0.5) {16};
|
||
|
\node[anchor=center,scale=0.8] at (3.5, 0.5) {17};
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (a) at (1,2.5) {53};
|
||
|
\path[draw,thick,-] (a) -- (0.5,1);
|
||
|
\path[draw,thick,-] (a) -- (1.5,1);
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (b) at (3,2.5) {33};
|
||
|
\path[draw,thick,-] (b) -- (2.5,1);
|
||
|
\path[draw,thick,-] (b) -- (3.5,1);
|
||
|
|
||
|
\node[draw, circle,scale=0.8,inner sep=1pt] (i) at (2,4.5) {86};
|
||
|
\path[draw,thick,-] (i) -- (a);
|
||
|
\path[draw,thick,-] (i) -- (b);
|
||
|
\end{scope}
|
||
|
\path[draw,thick,-] (2,19) -- (-6,16);
|
||
|
\path[draw,thick,-] (2,19) -- (10,16);
|
||
|
\path[draw,thick,-] (-6,9) -- (-10,6);
|
||
|
\path[draw,thick,-] (-6,9) -- (-2,6);
|
||
|
\path[draw,thick,-] (10,9) -- (6,6);
|
||
|
\path[draw,thick,-] (10,9) -- (14,6);
|
||
|
\end{tikzpicture}
|
||
|
\end{center}
|
||
|
|
||
|
Kaksiulotteisen
|
||
|
segmenttipuun operaatiot vievät aikaa
|
||
|
$O(\log^2 n)$,
|
||
|
koska suuressa puussa ja kussakin
|
||
|
pienessä puussa on $O(\log n)$ tasoa.
|
||
|
Segmenttipuu vie muistia $O(n^2)$,
|
||
|
koska jokainen pieni puu
|
||
|
vie muistia $O(n)$.
|
||
|
|
||
|
Vastaavalla tavalla voi luoda myös segmenttipuita,
|
||
|
joissa on vielä enemmän ulottuvuuksia,
|
||
|
mutta tälle on harvoin tarvetta.
|
||
|
|