Binary indexed tree
This commit is contained in:
parent
4781975ce0
commit
db7af1212d
174
luku09.tex
174
luku09.tex
|
@ -76,13 +76,13 @@ any possible range query efficiently.
|
||||||
\index{prefix sum array}
|
\index{prefix sum array}
|
||||||
|
|
||||||
Sum queries can be answered efficiently
|
Sum queries can be answered efficiently
|
||||||
by constructing a \key{prefix sum array}
|
by constructing a \key{sum array}
|
||||||
that contains the sum of the range $[1,k]$
|
that contains the sum of the range $[1,k]$
|
||||||
for each $k=1,2,\ldots,n$.
|
for each $k=1,2,\ldots,n$.
|
||||||
After this, the sum of any range $[a,b]$ of the
|
After this, the sum of any range $[a,b]$ of the
|
||||||
original array
|
original array
|
||||||
can be calculated in $O(1)$ time using the
|
can be calculated in $O(1)$ time using the
|
||||||
prefix sum array.
|
precalculated sum array.
|
||||||
|
|
||||||
For example, for the array
|
For example, for the array
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -110,7 +110,7 @@ For example, for the array
|
||||||
\node at (7.5,1.4) {$8$};
|
\node at (7.5,1.4) {$8$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
the corresponding prefix sum array is as follows:
|
the corresponding sum array is as follows:
|
||||||
\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);
|
||||||
|
@ -155,12 +155,12 @@ int sum(int a, int b) {
|
||||||
The function calculates the sum of range $[a,b]$
|
The function calculates the sum of range $[a,b]$
|
||||||
by subtracting the sum of range $[1,a-1]$
|
by subtracting the sum of range $[1,a-1]$
|
||||||
from the sum of range $[1,b]$.
|
from the sum of range $[1,b]$.
|
||||||
Thus, only two values from the prefix sum array
|
Thus, only two values from the sum array
|
||||||
are needed, and the query takes $O(1)$ time.
|
are needed, and the query takes $O(1)$ time.
|
||||||
Note that thanks to the one-based indexing,
|
Note that thanks to the one-based indexing,
|
||||||
the function also works when $a=1$ if $\texttt{s}[0]=0$.
|
the function also works when $a=1$ if $\texttt{s}[0]=0$.
|
||||||
|
|
||||||
As an example, let us consider the range $[4,7]$:
|
As an example, consider the range $[4,7]$:
|
||||||
\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);
|
||||||
|
@ -187,7 +187,7 @@ As an example, let us consider the range $[4,7]$:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
The sum of the range $[4,7]$ is $8+6+1+4=19$.
|
The sum of the range $[4,7]$ is $8+6+1+4=19$.
|
||||||
This can be calculated from the prefix sum array
|
This can be calculated from the sum array
|
||||||
using the sums $[1,3]$ and $[1,7]$:
|
using the sums $[1,3]$ and $[1,7]$:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -218,12 +218,12 @@ using the sums $[1,3]$ and $[1,7]$:
|
||||||
\end{center}
|
\end{center}
|
||||||
Thus, the sum of the range $[4,7]$ is $27-8=19$.
|
Thus, the sum of the range $[4,7]$ is $27-8=19$.
|
||||||
|
|
||||||
We can also generalize the idea of prefix sum array
|
We can also generalize the idea of a sum array
|
||||||
for a two-dimensional array.
|
for a two-dimensional array.
|
||||||
In this case, it will be possible to calculate the sum of
|
In this case, it will be possible to calculate the sum of
|
||||||
any rectangular subarray in $O(1)$ time.
|
any rectangular subarray in $O(1)$ time.
|
||||||
The prefix sum array will contain sums
|
The sum array will contain sums
|
||||||
of all subarrays that begin from the upper-left corner.
|
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:
|
||||||
|
@ -250,7 +250,7 @@ to the position of letter $X$.
|
||||||
\subsubsection{Minimum query}
|
\subsubsection{Minimum query}
|
||||||
|
|
||||||
It is also possible to answer a minimum query
|
It is also possible to answer a minimum query
|
||||||
in $O(1)$ after preprocessing, though it is
|
in $O(1)$ time after preprocessing, though it is
|
||||||
more difficult than answer a sum query.
|
more difficult than answer a sum query.
|
||||||
Note that minimum and maximum queries can always
|
Note that minimum and maximum queries can always
|
||||||
be implemented using same techniques,
|
be implemented using same techniques,
|
||||||
|
@ -438,36 +438,36 @@ The minimum of the range $[2,5]$ is 3,
|
||||||
and the minimum of the range $[4,7]$ is 1.
|
and the minimum of the range $[4,7]$ is 1.
|
||||||
Thus, the minimum of the range $[2,7]$ is 1.
|
Thus, the minimum of the range $[2,7]$ is 1.
|
||||||
|
|
||||||
\section{Binääri-indeksipuu}
|
\section{Binary indexed tree}
|
||||||
|
|
||||||
\index{binxxri-indeksipuu@binääri-indeksipuu}
|
\index{binary indexed tree}
|
||||||
\index{Fenwick-puu}
|
\index{Fenwick tree}
|
||||||
|
|
||||||
\key{Binääri-indeksipuu} eli \key{Fenwick-puu} on
|
A \key{binary indexed tree} or a \key{Fenwick tree}
|
||||||
summataulukkoa muistuttava tietorakenne,
|
is a data structure that resembles a sum array.
|
||||||
joka toteuttaa kaksi operaatiota:
|
The supported operations are answering
|
||||||
taulukon välin $[a,b]$ summakysely
|
a sum query for range $[a,b]$,
|
||||||
sekä taulukon kohdassa $k$ olevan luvun päivitys.
|
and updating the element at index $k$.
|
||||||
Kummankin operaation aikavaativuus on $O(\log n)$.
|
The time complexity for both of the operations is $O(\log n)$.
|
||||||
|
|
||||||
Binääri-indeksipuun etuna summataulukkoon verrattuna on,
|
Unlike a sum array, a binary indexed tree
|
||||||
että taulukkoa pystyy päivittämään tehokkaasti
|
can be efficiently updated between the sum queries.
|
||||||
summakyselyiden välissä.
|
This would not be possible using a sum array
|
||||||
Summataulukossa tämä ei olisi mahdollista,
|
because we should build the whole sum array again
|
||||||
vaan koko summataulukko tulisi muodostaa uudestaan $O(n)$-ajassa
|
in $O(n)$ time after each update.
|
||||||
taulukon päivityksen jälkeen.
|
|
||||||
|
|
||||||
\subsubsection{Rakenne}
|
\subsubsection{Structure}
|
||||||
|
|
||||||
Binääri-indeksipuu on taulukko, jonka
|
A binary indexed tree can be represented as an array
|
||||||
kohdassa $k$ on kohtaan $k$ päättyvän välin lukujen summa
|
where index $k$ contains the sum of a range in the
|
||||||
alkuperäisessä taulukossa.
|
original array that ends to index $k$.
|
||||||
Välin pituus on suurin 2:n potenssi, jolla $k$ on jaollinen.
|
The length of the range is the largest power of two
|
||||||
Esimerkiksi jos $k=6$, välin pituus on 2, koska
|
that divides $k$.
|
||||||
6 on jaollinen 2:lla mutta ei ole jaollinen 4:llä.
|
For example, if $k=6$, the length of the range is $2$
|
||||||
|
because $2$ divides $6$ but $4$ doesn't divide $6$.
|
||||||
|
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
Esimerkiksi taulukkoa
|
For example, for 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);
|
||||||
|
@ -493,7 +493,7 @@ Esimerkiksi taulukkoa
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
vastaava binääri-indeksipuu on seuraava:
|
the corresponding binary indexed tree is as follows:
|
||||||
\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);
|
||||||
|
@ -538,18 +538,21 @@ vastaava binääri-indeksipuu on seuraava:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Esimerkiksi binääri-indeksipuun kohdassa 6 on luku 7,
|
For example, the binary indexed tree
|
||||||
koska välin $[5,6]$ lukujen summa on $6+1=7$.
|
contains the value 7 at index 6
|
||||||
|
because the sum of the elements in the range $[5,6]$
|
||||||
|
of the original array is $6+1=7$.
|
||||||
|
|
||||||
\subsubsection{Summakysely}
|
\subsubsection{Sum query}
|
||||||
|
|
||||||
Binääri-indeksipuun perusoperaatio on välin $[1,k]$
|
The basic operation in a binary indexed tree is
|
||||||
summan laskeminen, missä $k$ on mikä tahansa taulukon kohta.
|
calculating the sum of a range $[1,k]$ where $k$
|
||||||
Tällaisen summan pystyy muodostamaan aina laskemalla yhteen
|
is any index in the array.
|
||||||
puussa olevia välien summia.
|
The sum of any range can be constructed by combining
|
||||||
|
sums of subranges in the tree.
|
||||||
|
|
||||||
Esimerkiksi välin $[1,7]$
|
For example, the range $[1,7]$ will be divided
|
||||||
summa muodostuu seuraavista summista:
|
into three subranges:
|
||||||
\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);
|
||||||
|
@ -594,25 +597,26 @@ summa muodostuu seuraavista summista:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Välin $[1,7]$ summa on siis $16+7+4=27$.
|
Thus, the sum of the range $[1,7]$ is $16+7+4=27$.
|
||||||
Binääri-indeksipuun rakenteen ansiosta
|
Because of the structure of the binary indexed tree,
|
||||||
jokainen summaan kuuluva väli on eripituinen.
|
the length of each subrange inside a range is distinct,
|
||||||
Niinpä summa muodostuu aina $O(\log n)$ välin summasta.
|
so the sum of a range
|
||||||
|
always consists of sums of $O(\log n)$ subranges.
|
||||||
|
|
||||||
Summataulukon tavoin binääri-indeksipuusta voi laskea
|
Using the same technique that we previously used
|
||||||
tehokkaasti minkä tahansa taulukon välin summan,
|
with a sum array,
|
||||||
koska välin $[a,b]$ summa saadaan vähentämällä
|
we can efficiently calculate the sum of any range
|
||||||
välin $[1,b]$ summasta välin $[1,a-1]$ summa.
|
$[a,b]$ by substracting the sum of the range $[1,a-1]$
|
||||||
Aikavaativuus on edelleen $O(\log n)$,
|
from the sum of the range $[1,b]$.
|
||||||
koska riittää laskea kaksi $[1,k]$-välin summaa.
|
The time complexity remains $O(\log n)$
|
||||||
|
because it suffices to calculate two sums of $[1,k]$ ranges.
|
||||||
|
|
||||||
\subsubsection{Taulukon päivitys}
|
\subsubsection{Array update}
|
||||||
|
|
||||||
Kun taulukon kohdassa $k$ oleva luku muuttuu,
|
When an element in the original array changes,
|
||||||
tämä vaikuttaa useaan
|
several sums in the binary indexed tree change.
|
||||||
binääri-indeksi\-puussa olevaan summaan.
|
For example, if the value at index 3 changes,
|
||||||
Esimerkiksi jos kohdassa 3 oleva luku muuttuu,
|
the sums of the following ranges change:
|
||||||
seuraavat välien summat muuttuvat:
|
|
||||||
\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);
|
||||||
|
@ -657,33 +661,30 @@ seuraavat välien summat muuttuvat:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Myös tässä tapauksessa kaikki välit,
|
Also in this case, the length of each range is distinct,
|
||||||
joihin muutos vaikuttaa, ovat eripituisia,
|
so $O(\log n)$ ranges will be updated in the binary indexed tree.
|
||||||
joten muutos kohdistuu $O(\log n)$ kohtaan
|
|
||||||
binääri-indeksipuussa.
|
|
||||||
|
|
||||||
\subsubsection{Toteutus}
|
\subsubsection{Implementation}
|
||||||
|
|
||||||
Binääri-indeksipuun operaatiot on mahdollista toteuttaa
|
The operations of a binary indexed tree can be implemented
|
||||||
lyhyesti ja tehokkaasti bittien käsittelyn avulla.
|
in an elegant and efficient way using bit manipulation.
|
||||||
Oleellinen bittioperaatio on $k \& -k$,
|
The bit operation needed is $k \& -k$ that
|
||||||
joka eristää luvusta $k$ viimeisenä olevan ykkösbitin.
|
returns the last bit one from number $k$.
|
||||||
Esimerkiksi $6 \& -6=2$, koska luku $6$ on bittimuodossa 110
|
For example, $6 \& -6=2$ because the number $6$
|
||||||
ja luku $2$ on bittimuodossa on 10.
|
corresponds to 110 and the number $2$ corresponds to 10.
|
||||||
|
|
||||||
Osoittautuu, että summan laskemisessa
|
It turns out that when calculating a range sum,
|
||||||
binääri-indeksipuun kohtaa $k$ tulee muuttaa joka askeleella niin,
|
the index $k$ in the binary indexed tree should be
|
||||||
että siitä poistetaan luku $k \& -k$.
|
decreased by $k \& -k$ at every step.
|
||||||
Vastaavasti taulukon päivityksessä kohtaa $k$ tulee muuttaa joka askeleella niin,
|
Correspondingly, when updating the array,
|
||||||
että siihen lisätään luku $k \& -k$.
|
the index $k$ should be increased by $k \& -k$ at every step.
|
||||||
|
|
||||||
|
The following functions assume that the binary indexed tree
|
||||||
|
is stored to array \texttt{b} and it consists of indices $1 \ldots n$.
|
||||||
|
|
||||||
Seuraavat funktiot olettavat, että binääri-indeksipuu on
|
The function \texttt{sum} calculates the sum of the range $[1,k]$:
|
||||||
tallennettu taulukkoon \texttt{b} ja se muodostuu kohdista $1 \ldots n$.
|
|
||||||
|
|
||||||
Funktio \texttt{summa} laskee välin $[1,k]$ summan:
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int summa(int k) {
|
int sum(int k) {
|
||||||
int s = 0;
|
int s = 0;
|
||||||
while (k >= 1) {
|
while (k >= 1) {
|
||||||
s += b[k];
|
s += b[k];
|
||||||
|
@ -693,9 +694,9 @@ int summa(int k) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Funktio \texttt{lisaa} kasvattaa taulukon kohtaa $k$ arvolla $x$:
|
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) {
|
||||||
while (k <= n) {
|
while (k <= n) {
|
||||||
b[k] += x;
|
b[k] += x;
|
||||||
k += k&-k;
|
k += k&-k;
|
||||||
|
@ -703,10 +704,11 @@ void lisaa(int k, int x) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Kummankin yllä olevan funktion aikavaativuus on $O(\log n)$,
|
The time complexity of both above functions is
|
||||||
koska funktiot muuttavat $O(\log n)$ kohtaa
|
$O(\log n)$ because the functions change $O(\log n)$
|
||||||
binääri-indeksipuussa ja uuteen kohtaan siirtyminen
|
values in the binary indexed tree and each move
|
||||||
vie aikaa $O(1)$ bittioperaation avulla.
|
to the next index
|
||||||
|
takes $O(1)$ time using the bit operation.
|
||||||
|
|
||||||
\section{Segmenttipuu}
|
\section{Segmenttipuu}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue