1171 lines
37 KiB
TeX
1171 lines
37 KiB
TeX
\chapter{Segment trees revisited}
|
|
|
|
\index{segment tree}
|
|
|
|
A segment tree is a versatile data structure
|
|
that can be used in many different situations.
|
|
However, there are many topics related to segment trees
|
|
that we haven't touched yet.
|
|
Now it's time to learn some more advanced variations
|
|
of segment trees and see their full potential.
|
|
|
|
So far, we have implemented the operations
|
|
of a segment tree by walking \emph{from the bottom to the top},
|
|
from the leaves to the root.
|
|
For example, we have calculated the sum of a range $[a,b]$
|
|
as follows (Chapter 9.3):
|
|
|
|
\begin{lstlisting}
|
|
int sum(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}
|
|
However, in more advanced segment trees,
|
|
it's beneficial to implement the operations
|
|
in another way, \emph{from the top to the bottom},
|
|
from the root to the leaves.
|
|
Using this approach, the function becomes as follows:
|
|
|
|
\begin{lstlisting}
|
|
int sum(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 sum(a, min(x+d-1,b), 2*k, x, x+d-1) +
|
|
sum(max(x+d,a), b, 2*k+1, x+d, y);
|
|
}
|
|
\end{lstlisting}
|
|
Now we can calulate the sum of the range $[a,b]$
|
|
as follows:
|
|
|
|
\begin{lstlisting}
|
|
int s = sum(a, b, 1, 0, N-1);
|
|
\end{lstlisting}
|
|
The parameter $k$ is the current position
|
|
in array \texttt{p}.
|
|
Initially $k$ equals 1, because we begin
|
|
at the root of the segment tree.
|
|
The range $[x,y]$ corresponds to $k$,
|
|
and is initially $[0,N-1]$.
|
|
If $[a,b]$ is outside $[x,y]$,
|
|
the sum of the range is 0,
|
|
and if $[a,b]$ equals $[x,y]$,
|
|
the sum can be found in array \texttt{p}.
|
|
If $[a,b]$ is completely or partially inside $[x,y]$,
|
|
the search continues recursively to the
|
|
left and right half of $[x,y]$.
|
|
The size of both halves is $d=\frac{1}{2}(y-x+1)$;
|
|
the left half is $[x,x+d-1]$
|
|
and the right half is $[x+d,y]$.
|
|
|
|
The following picture shows how the search proceeds
|
|
when calculating the sum of the marked elements.
|
|
The gray nodes indicate nodes where the recursion
|
|
stops and the sum of the range can be found in array \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}
|
|
Also in this implementation,
|
|
the time complexity of a range query is $O(\log n)$,
|
|
because the total number of processed nodes is $O(\log n)$.
|
|
|
|
\section{Lazy propagation}
|
|
|
|
\index{lazy propagation}
|
|
\index{lazy segment tree}
|
|
|
|
Using \key{lazy propagation}, we can construct
|
|
a segment tree that supports both range updates
|
|
and range queries in $O(\log n)$ time.
|
|
The idea is to perform the updates and queries
|
|
from the top to the bottom, and process the updates
|
|
\emph{lazily} so that they are propagated
|
|
down the tree only when it is necessary.
|
|
|
|
In a lazy segment tree, nodes contain two types of
|
|
information.
|
|
Like in a normal segment tree,
|
|
each node contains the sum or some other value
|
|
of the corresponding subarray.
|
|
In addition, the node may contain information
|
|
related to lazy updates, which has not been
|
|
propagated yet to its children.
|
|
|
|
There are two possible types for range updates:
|
|
\emph{addition} and \emph{insertion}.
|
|
In addition, each element in the range is
|
|
increased by some value,
|
|
and in insertion, each element in the range
|
|
is assigned some value.
|
|
Both operations can be implemented using
|
|
similar ideas, and it's possible to construct
|
|
a tree that supports both the operations
|
|
simultaneously.
|
|
|
|
\subsubsection{Lazy segment tree}
|
|
|
|
Let's consider an example where our goal is to
|
|
construct a segment tree that supports the following operations:
|
|
|
|
\begin{itemize}
|
|
\item increase each element in $[a,b]$ by $u$
|
|
\item calculate the sum of elements in $[a,b]$
|
|
\end{itemize}
|
|
We will construct a tree where each node
|
|
contains two values $s/z$:
|
|
$s$ denotes the sum of elements in the range,
|
|
like in a standard segment tree,
|
|
and $z$ denotes a lazy update,
|
|
which means that all elements in the range
|
|
should be increased by $z$.
|
|
In the following tree, $z=0$ for all nodes,
|
|
so there are no lazy updates.
|
|
\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}
|
|
|
|
When a range $[a,b]$ is increased by $u$,
|
|
we walk from the root towards the leaves
|
|
and modify the nodes in the tree as follows:
|
|
If the range $[x,y]$ of a node is
|
|
completely inside the range $[a,b]$,
|
|
we increase the $z$ value of the node by $u$ and stop.
|
|
However, if $[x,y]$ only partially belongs to $[a,b]$,
|
|
we increase the $s$ value of the node by $hu$,
|
|
where $h$ is the size of the intersection of $[a,b]$
|
|
and $[x,y]$, and continue our walk recursively in the tree.
|
|
|
|
For example, the following picture shows the tree after
|
|
increasing the elements in the range marked at the bottom by 2:
|
|
\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}
|
|
|
|
We also calculate the sum in a range $[a,b]$
|
|
by walking in the tree from the root towards the leaves.
|
|
If the range $[x,y]$ of a node completely belongs
|
|
to $[a,b]$, we add the $s$ value of the node to the sum.
|
|
Otherwise, we continue the search recursively
|
|
downwards in the tree.
|
|
|
|
Always before processing a node,
|
|
the value of the lazy update is propagated
|
|
to the children of the node.
|
|
This happens both in a range update
|
|
and a range query.
|
|
The idea is that the lazy update will be propagated
|
|
downwards only when it is necessary,
|
|
so that the operations are always efficient.
|
|
|
|
The following picture shows how the tree changes
|
|
when we calculate the sum in the marked range:
|
|
\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}
|
|
The result of this query was that a lazy update was
|
|
propagated downwards in the nodes that are inside the rectangle.
|
|
It was necessary to propagate the lazy update,
|
|
because some of the updated elements were inside the range.
|
|
|
|
Note that sometimes it's necessary to combine lazy updates.
|
|
This happens when a node already has a lazy update,
|
|
and another lazy update will be added to it.
|
|
In the above tree, it's easy to combine lazy updates
|
|
because updates $z_1$ and $z_2$ combined equal to update $z_1+z_2$.
|
|
|
|
\subsubsection{Polynomial update}
|
|
|
|
A lazy update can be generalized so that it's
|
|
allowed to update a range by a polynomial
|
|
\[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\]
|
|
|
|
Here, the update for the first element in the range is $p(0)$,
|
|
for the second element $p(1)$, etc., so the update
|
|
at index $i$ in range $[a,b]$ is $p(i-a)$.
|
|
For example, adding a polynomial $p(u)=u+1$
|
|
to range $[a,b]$ means that the element at index $a$
|
|
increases by 1, the element at index $a+1$
|
|
increases by 2, etc.
|
|
|
|
A polynomial update can be supported by
|
|
storing $k+2$ values to each node where $k$
|
|
equals the degree of the polynomial.
|
|
The value $s$ is the sum of the elements in the range,
|
|
and values $z_0,z_1,\ldots,z_k$ are the coefficients
|
|
of a polynomial that corresponds to a lazy update.
|
|
|
|
Now, the sum of $[x,y]$ is
|
|
\[s+\sum_{u=0}^{y-x} z_k u^k + z_{k-1} u^{k-1} + \cdots + z_0,\]
|
|
that can be efficiently calculated using sum formulas
|
|
For example, the value $z_0$ corresponds to the sum
|
|
$(y-x+1)z_0$, and the value $z_1 u$ corresponds to the sum
|
|
\[z_1(0+1+\cdots+y-x) = z_1 \frac{(y-x)(y-x+1)}{2} .\]
|
|
|
|
When propagating an update in the tree,
|
|
the indices of the polynomial $p(u)$ change,
|
|
because in each range $[x,y]$,
|
|
the values are
|
|
calculated for $x=0,1,\ldots,y-x$.
|
|
However, this is not a problem, because
|
|
$p'(u)=p(u+h)$ is a polynomial
|
|
of equal degree as $p(u)$.
|
|
For example, if $p(u)=t_2 u^2+t_1 u-t_0$, then
|
|
\[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.
|
|
|