Segment tree
This commit is contained in:
parent
db7af1212d
commit
fe87f1f1e3
300
luku09.tex
300
luku09.tex
|
@ -228,7 +228,7 @@ for all subarrays that begin from the upper-left corner.
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
The following picture illustrates the idea:
|
The following picture illustrates the idea:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.55]
|
\begin{tikzpicture}[scale=0.54]
|
||||||
\draw[fill=lightgray] (3,2) rectangle (7,5);
|
\draw[fill=lightgray] (3,2) rectangle (7,5);
|
||||||
\draw (0,0) grid (10,7);
|
\draw (0,0) grid (10,7);
|
||||||
%\draw[line width=2pt] (3,2) rectangle (7,5);
|
%\draw[line width=2pt] (3,2) rectangle (7,5);
|
||||||
|
@ -710,36 +710,38 @@ values in the binary indexed tree and each move
|
||||||
to the next index
|
to the next index
|
||||||
takes $O(1)$ time using the bit operation.
|
takes $O(1)$ time using the bit operation.
|
||||||
|
|
||||||
\section{Segmenttipuu}
|
\section{Segment tree}
|
||||||
|
|
||||||
\index{segmenttipuu@segmenttipuu}
|
\index{segment tree}
|
||||||
|
|
||||||
\key{Segmenttipuu} on tietorakenne,
|
A \key{segment tree} is a data structure
|
||||||
jonka operaatiot ovat taulukon välin $[a,b]$ välikysely
|
whose supported operations are
|
||||||
sekä kohdan $k$ arvon päivitys.
|
handling a range query for range $[a,b]$
|
||||||
Segmenttipuun avulla voi toteuttaa summakyselyn,
|
and updating the element at index $k$.
|
||||||
minimikyselyn ja monia muitakin kyselyitä niin,
|
Using a segment tree, we can implement sum
|
||||||
että kummankin operaation aikavaativuus on $O(\log n)$.
|
queries, minimum queries and many other
|
||||||
|
queries so that both operations work in $O(\log n)$ time.
|
||||||
|
|
||||||
Segmenttipuun etuna binääri-indeksipuuhun verrattuna on,
|
Compared to a binary indexed tree,
|
||||||
että se on yleisempi tietorakenne.
|
the advantage of a segment tree is that it is
|
||||||
Binääri-indeksipuulla voi toteuttaa vain summakyselyn,
|
a more general data structure.
|
||||||
mutta segmenttipuu sallii muitakin kyselyitä.
|
While binary indexed trees only support
|
||||||
Toisaalta segmenttipuu vie enemmän muistia ja
|
sum queries, segment trees also support other queries.
|
||||||
on hieman vaikeampi toteuttaa kuin binääri-indeksipuu.
|
On the other hand, a segment tree requires more
|
||||||
|
memory and is a bit more difficult to implement.
|
||||||
|
|
||||||
\subsubsection{Rakenne}
|
\subsubsection{Structure}
|
||||||
|
|
||||||
Segmenttipuussa on $2n-1$ solmua niin,
|
A segment tree contains $2n-1$ nodes
|
||||||
että alimmalla tasolla on $n$ solmua,
|
so that the bottom $n$ nodes correspond
|
||||||
jotka kuvaavat taulukon sisällön,
|
to the original array and the other nodes
|
||||||
ja ylemmillä tasoilla on välikyselyihin
|
contain information needed for range queries.
|
||||||
tarvittavaa tietoa.
|
The values in a segment tree depend on
|
||||||
Segmenttipuun sisältö riippuu siitä,
|
the supported query type.
|
||||||
mikä välikysely puun tulee toteuttaa.
|
We will first assume that the supported
|
||||||
Oletamme aluksi, että välikysely on tuttu summakysely.
|
query is the sum query.
|
||||||
|
|
||||||
Esimerkiksi taulukkoa
|
For example, the array
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -764,7 +766,7 @@ Esimerkiksi taulukkoa
|
||||||
\node at (7.5,1.4) {$8$};
|
\node at (7.5,1.4) {$8$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
vastaa seuraava segmenttipuu:
|
corresponds to the following segment tree:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -804,34 +806,32 @@ vastaa seuraava segmenttipuu:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Jokaisessa segmenttipuun solmussa on tietoa
|
Each internal node in the segment tree contains
|
||||||
$2^k$-kokoisesta välistä taulukossa.
|
information about a range of size $2^k$
|
||||||
Tässä tapauksessa solmussa oleva arvo kertoo,
|
in the original array.
|
||||||
mikä on taulukon lukujen summa solmua vastaavalla
|
In the above tree, the value of each internal
|
||||||
välillä.
|
node is the sum of the corresponding array elements,
|
||||||
Kunkin solmun arvo saadaan laskemalla yhteen
|
and it can be calculated as the sum of
|
||||||
solmun alapuolella vasemmalla ja oikealla
|
the values of its left and right child node.
|
||||||
olevien solmujen arvot.
|
|
||||||
|
|
||||||
Segmenttipuu on mukavinta rakentaa niin,
|
It is convenient to build a segment tree
|
||||||
että taulukon koko on 2:n potenssi,
|
when the size of the array is a power of two
|
||||||
jolloin tuloksena on täydellinen binääripuu.
|
and the tree is a complete binary tree.
|
||||||
Jatkossa oletamme aina,
|
In the sequel, we will assume that the tree
|
||||||
että taulukko täyttää tämän vaatimuksen.
|
is built like this.
|
||||||
Jos taulukon koko ei ole 2:n potenssi,
|
If the size of the array is not a power of two,
|
||||||
sen loppuun voi lisätä tyhjää niin,
|
we can always extend it using zero elements.
|
||||||
että koosta tulee 2:n potenssi.
|
|
||||||
|
|
||||||
\subsubsection{Välikysely}
|
\subsubsection{Range query}
|
||||||
|
|
||||||
Segmenttipuussa vastaus välikyselyyn lasketaan
|
In a segment tree, the answer for a range query
|
||||||
väliin kuuluvista solmuista,
|
is calculated from nodes that belong to the range
|
||||||
jotka ovat mahdollisimman korkealla puussa.
|
and are as high as possible in the tree.
|
||||||
Jokainen solmu antaa vastauksen väliin kuuluvalle osavälille,
|
Each node gives the answer for a subrange,
|
||||||
ja vastaus kyselyyn selviää yhdistämällä
|
and the answer for the entire range can be
|
||||||
segmenttipuusta saadut osavälejä koskeva tiedot.
|
calculated by combining these values.
|
||||||
|
|
||||||
Tarkastellaan esimerkiksi seuraavaa taulukon väliä:
|
For example, consider the following range:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\fill[color=gray!50] (2,0) rectangle (8,1);
|
\fill[color=gray!50] (2,0) rectangle (8,1);
|
||||||
|
@ -857,9 +857,10 @@ Tarkastellaan esimerkiksi seuraavaa taulukon väliä:
|
||||||
\node at (7.5,1.4) {$8$};
|
\node at (7.5,1.4) {$8$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Lukujen summa välillä $[3,8]$ on $6+3+2+7+2+6=26$.
|
The sum of elements in the range $[3,8]$ is
|
||||||
Segmenttipuusta summa saadaan laskettua seuraavien
|
$6+3+2+7+2+6=26$.
|
||||||
osasummien avulla:
|
The sum can be calculated from the segment tree
|
||||||
|
using the following subranges:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -898,27 +899,26 @@ osasummien avulla:
|
||||||
\path[draw,thick,-] (m) -- (j);
|
\path[draw,thick,-] (m) -- (j);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Taulukon välin summaksi tulee osasummista $9+17=26$.
|
Thus, the sum of the range is $9+17=26$.
|
||||||
|
|
||||||
Kun vastaus välikyselyyn lasketaan mahdollisimman
|
When the answer for a range query is
|
||||||
korkealla segmenttipuussa olevista solmuista,
|
calculated using as high nodes as possible,
|
||||||
väliin kuuluu enintään kaksi solmua
|
at most two nodes on each level
|
||||||
jokaiselta segmenttipuun tasolta.
|
of the segment tree are needed.
|
||||||
Tämän ansiosta välikyselyssä
|
Because of this, the total number of nodes
|
||||||
tarvittavien solmujen yhteismäärä on vain $O(\log n)$.
|
examined is only $O(\log n)$.
|
||||||
|
|
||||||
\subsubsection{Taulukon päivitys}
|
\subsubsection{Array update}
|
||||||
|
|
||||||
Kun taulukossa oleva arvo muuttuu,
|
When an element in the array changes,
|
||||||
segmenttipuussa täytyy päivittää
|
we should update all nodes in the segment tree
|
||||||
kaikkia solmuja, joiden arvo
|
whose value depends on the changed element.
|
||||||
riippuu muutetusta taulukon kohdasta.
|
This can be done by travelling from the bottom
|
||||||
Tämä tapahtuu kulkemalla puuta ylöspäin huipulle
|
to the top in the tree and updating the nodes along the path.
|
||||||
asti ja tekemällä muutokset.
|
|
||||||
|
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
Seuraava kuva näyttää, mitkä solmut segmenttipuussa muuttuvat,
|
The following picture shows which nodes in the segment tree
|
||||||
jos taulukon luku 7 muuttuu.
|
change if the element 7 in the array changes.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -961,28 +961,26 @@ jos taulukon luku 7 muuttuu.
|
||||||
\end{center}
|
\end{center}
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
|
|
||||||
Polku segmenttipuun pohjalta huipulle muodostuu aina $O(\log n)$ solmusta,
|
The path from the bottom of the segment tree to the top
|
||||||
joten taulukon arvon muuttuminen vaikuttaa $O(\log n)$ solmuun puussa.
|
always consists of $O(\log n)$ nodes,
|
||||||
|
so updating the array affects $O(\log n)$ nodes in the tree.
|
||||||
|
|
||||||
\subsubsection{Puun tallennus}
|
\subsubsection{Storing the tree}
|
||||||
|
|
||||||
Segmenttipuun voi tallentaa muistiin
|
A segment tree can be stored as an array
|
||||||
$2N$ alkion taulukkona,
|
of $2N$ elements where $N$ is a power of two.
|
||||||
jossa $N$ on riittävän suuri 2:n potenssi.
|
From now on, we will assume that the indices
|
||||||
Tällaisen segmenttipuun avulla voi ylläpitää
|
of the original array are between $0$ and $N-1$.
|
||||||
taulukkoa, jonka indeksialue on $[0,N-1]$.
|
|
||||||
|
|
||||||
Segmenttipuun taulukon
|
The element at index 1 in the segment tree array
|
||||||
kohdassa 1 on puun ylimmän solmun arvo,
|
contains the top node of the tree,
|
||||||
kohdat 2 ja 3 sisältävät seuraavan tason
|
the elements at indices 2 and 3 correspond to
|
||||||
solmujen arvot, jne.
|
the second level of the tree, and so on.
|
||||||
Segmenttipuun alin taso eli varsinainen
|
Finally, the elements beginning from index $N$
|
||||||
taulukon sisältä tallennetaan
|
contain the bottom level of the tree, i.e.,
|
||||||
kohdasta $N$ alkaen.
|
the actual content of the original array.
|
||||||
Niinpä taulukon kohdassa $k$ oleva alkio
|
|
||||||
on segmenttipuun taulukossa kohdassa $k+N$.
|
|
||||||
|
|
||||||
Esimerkiksi segmenttipuun
|
For example, the segment tree
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -1021,7 +1019,7 @@ Esimerkiksi segmenttipuun
|
||||||
\path[draw,thick,-] (m) -- (j);
|
\path[draw,thick,-] (m) -- (j);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
voi tallentaa taulukkoon seuraavasti ($N=8$):
|
can be stored as follows ($N=8$):
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
%\fill[color=lightgray] (3,0) rectangle (7,1);
|
%\fill[color=lightgray] (3,0) rectangle (7,1);
|
||||||
|
@ -1061,30 +1059,24 @@ voi tallentaa taulukkoon seuraavasti ($N=8$):
|
||||||
\node at (14.5,1.4) {$15$};
|
\node at (14.5,1.4) {$15$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Tätä tallennustapaa käyttäen kohdassa $k$
|
Using this representation,
|
||||||
olevalle solmulle pätee, että
|
for a node at index $k$,
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item ylempi solmu on kohdassa $\lfloor k/2 \rfloor$,
|
\item the parent node is at index $\lfloor k/2 \rfloor$,
|
||||||
\item vasen alempi solmu on kohdassa $2k$ ja
|
\item the left child node is at index $2k$, and
|
||||||
\item oikea alempi solmu on kohdassa $2k+1$.
|
\item the right child node is at index $2k+1$.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
Huomaa, että tämän seurauksena solmun kohta on parillinen,
|
Note that this implies that the index of a node
|
||||||
jos se on vasemmalla ylemmästä solmusta katsoen,
|
is even if it is a left child and odd if it is a right child.
|
||||||
ja pariton, jos se on oikealla.
|
|
||||||
|
|
||||||
\subsubsection{Toteutus}
|
\subsubsection{Functions}
|
||||||
|
|
||||||
Tarkastellaan seuraavaksi välikyselyn ja päivityksen
|
We assume that the segment tree is stored
|
||||||
toteutusta segmenttipuuhun.
|
in the array \texttt{p}.
|
||||||
Seuraavat funktiot olettavat, että segmenttipuu
|
The following function calculates the sum of range $[a,b]$:
|
||||||
on tallennettu $2n-1$-kokoi\-seen taulukkoon $\texttt{p}$
|
|
||||||
edellä kuvatulla tavalla.
|
|
||||||
|
|
||||||
Funktio \texttt{summa} laskee summan
|
|
||||||
välillä $a \ldots b$:
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int summa(int a, int b) {
|
int sum(int a, int b) {
|
||||||
a += N; b += N;
|
a += N; b += N;
|
||||||
int s = 0;
|
int s = 0;
|
||||||
while (a <= b) {
|
while (a <= b) {
|
||||||
|
@ -1096,17 +1088,18 @@ int summa(int a, int b) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Funktio aloittaa summan laskeminen segmenttipuun
|
The function begins from the bottom of the tree
|
||||||
pohjalta ja liikkuu askel kerrallaan ylemmille tasoille.
|
and moves step by step upwards in the tree.
|
||||||
Funktio laskee välin summan muuttujaan $s$
|
The function calculates the range sum to
|
||||||
yhdistämällä puussa olevia osasummia.
|
the variable $s$ by combining the sums in the tree nodes.
|
||||||
Välin reunalla oleva osasumma lisätään summaan
|
The value of a node is added to the sum if
|
||||||
aina silloin, kun se ei kuulu ylemmän tason osasummaan.
|
the parent node doesn't belong to the range.
|
||||||
|
|
||||||
Funktio \texttt{lisaa} kasvattaa kohdan $k$ arvoa $x$:llä:
|
The function \texttt{add} increases the value
|
||||||
|
of element $k$ by $x$:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
void lisaa(int k, int x) {
|
void add(int k, int x) {
|
||||||
k += N;
|
k += N;
|
||||||
p[k] += x;
|
p[k] += x;
|
||||||
for (k /= 2; k >= 1; k /= 2) {
|
for (k /= 2; k >= 1; k /= 2) {
|
||||||
|
@ -1114,35 +1107,32 @@ void lisaa(int k, int x) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
Ensin funktio tekee muutoksen puun alimmalle
|
First the function updates the bottom level
|
||||||
tasolle taulukkoon.
|
of the tree that corresponds to the original array.
|
||||||
Tämän jälkeen se päivittää kaikki osasummat
|
After this, the function updates the values of all
|
||||||
puun huipulle asti.
|
internal nodes in the tree, until it reaches
|
||||||
Taulukon \texttt{p} indeksoinnin ansiosta
|
the root node of the tree.
|
||||||
kohdasta $k$ alemmalla tasolla
|
|
||||||
ovat kohdat $2k$ ja $2k+1$.
|
|
||||||
|
|
||||||
Molemmat segmenttipuun operaatiot toimivat ajassa
|
Both operations in the segment tree work
|
||||||
$O(\log n)$, koska $n$ lukua sisältävässä
|
in $O(\log n)$ time because a segment tree
|
||||||
segmenttipuussa on $O(\log n)$ tasoa
|
of $n$ elements consists of $O(\log n)$ levels,
|
||||||
ja operaatiot siirtyvät askel kerrallaan
|
and the operations move one level forward at each step.
|
||||||
segmenttipuun tasoja ylöspäin.
|
|
||||||
|
|
||||||
\subsubsection{Muut kyselyt}
|
\subsubsection{Other queries}
|
||||||
|
|
||||||
Segmenttipuu mahdollistaa summan lisäksi minkä
|
Besides the sum query,
|
||||||
tahansa välikyselyn,
|
the segment tree can support any range query
|
||||||
jossa vierekkäisten välien $[a,b]$ ja $[b+1,c]$
|
where the answer for range $[a,b]$
|
||||||
tuloksista pystyy laskemaan tehokkaasti
|
can be efficiently calculated
|
||||||
välin $[a,c]$ tuloksen.
|
from ranges $[a,c]$ and $[c+1,b]$ where
|
||||||
Tällaisia kyselyitä
|
$c$ is some element between $a$ and $b$.
|
||||||
ovat esimerkiksi minimi ja maksimi,
|
Such queries are, for example,
|
||||||
suurin yhteinen tekijä
|
minimum and maximum, greatest common divisor,
|
||||||
sekä bittioperaatiot and, or ja xor.
|
and bit operations.
|
||||||
|
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
Esimerkiksi seuraavan segmenttipuun avulla voi laskea
|
For example, the following segment tree
|
||||||
taulukon välien minimejä:
|
supports minimum queries:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -1184,27 +1174,25 @@ taulukon välien minimejä:
|
||||||
\end{center}
|
\end{center}
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
|
|
||||||
Tässä segmenttipuussa jokainen puun solmu kertoo,
|
In this segment tree, every node in the tree
|
||||||
mikä on pienin luku sen alapuolella olevassa
|
contains the smallest element in the corresponding
|
||||||
taulukon osassa.
|
range of the original array.
|
||||||
Segmenttipuun ylin luku on pienin luku
|
The top node of the tree contains the smallest
|
||||||
koko taulukon alueella.
|
element in the array.
|
||||||
Puun toteutus on samanlainen kuin summan laskemisessa,
|
The tree can be implemented like previously,
|
||||||
mutta joka kohdassa pitää laskea summan sijasta
|
but instead of sums, minima are calculated.
|
||||||
lukujen minimi.
|
|
||||||
|
|
||||||
\subsubsection{Binäärihaku puussa}
|
\subsubsection{Binary search in tree}
|
||||||
|
|
||||||
Segmenttipuun sisältämää tietoa voi käyttää
|
The structure of the segment tree makes it possible
|
||||||
binäärihaun kaltaisesti aloittamalla
|
to use binary search.
|
||||||
haun puun huipulta.
|
For example, if the tree supports the minimum query,
|
||||||
Näin on mahdollista selvittää esimerkiksi
|
we can find the index of the smallest
|
||||||
minimisegmenttipuusta $O(\log n)$-ajassa,
|
element in $O(\log n)$ time.
|
||||||
missä kohdassa on taulukon pienin luku.
|
|
||||||
|
|
||||||
Esimerkiksi seuraavassa puussa pienin
|
For example, in the following tree the
|
||||||
alkio on 1, jonka sijainti löytyy
|
smallest element is 1 that can be found
|
||||||
kulkemalla puussa huipulta alaspäin:
|
by following a path downwards from the top node:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
|
Loading…
Reference in New Issue