Improve language
This commit is contained in:
parent
6a9049f35b
commit
7c09ec17d3
121
chapter28.tex
121
chapter28.tex
|
@ -3,7 +3,7 @@
|
||||||
\index{segment tree}
|
\index{segment tree}
|
||||||
|
|
||||||
A segment tree is a versatile data structure
|
A segment tree is a versatile data structure
|
||||||
that can be used in a large number of problems.
|
that can be used to solve a large number of algorithm problems.
|
||||||
However, there are many topics related to segment trees
|
However, there are many topics related to segment trees
|
||||||
that we have not touched yet.
|
that we have not touched yet.
|
||||||
Now is time to discuss some more advanced variants
|
Now is time to discuss some more advanced variants
|
||||||
|
@ -12,17 +12,16 @@ of segment trees.
|
||||||
So far, we have implemented the operations
|
So far, we have implemented the operations
|
||||||
of a segment tree by walking \emph{from bottom to top}
|
of a segment tree by walking \emph{from bottom to top}
|
||||||
in the tree.
|
in the tree.
|
||||||
For example, we have calculated the sum of
|
For example, we have calculated
|
||||||
elements in a range $[a,b]$
|
range sums as follows (Chapter 9.3):
|
||||||
as follows (Chapter 9.3):
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int sum(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) {
|
||||||
if (a%2 == 1) s += p[a++];
|
if (a%2 == 1) s += tree[a++];
|
||||||
if (b%2 == 0) s += p[b--];
|
if (b%2 == 0) s += tree[b--];
|
||||||
a /= 2; b /= 2;
|
a /= 2; b /= 2;
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
|
@ -30,35 +29,35 @@ int sum(int a, int b) {
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
However, in more advanced segment trees,
|
However, in more advanced segment trees,
|
||||||
it is often needed to implement the operations
|
it is often necessary to implement the operations
|
||||||
in another way, \emph{from top to bottom}.
|
in another way, \emph{from top to bottom}.
|
||||||
Using this approach, the function becomes as follows:
|
Using this approach, the function becomes as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int sum(int a, int b, int k, int x, int y) {
|
int sum(int a, int b, int k, int x, int y) {
|
||||||
if (b < x || a > y) return 0;
|
if (b < x || a > y) return 0;
|
||||||
if (a <= x && y <= b) return p[k];
|
if (a <= x && y <= b) return tree[k];
|
||||||
int d = (x+y)/2;
|
int d = (x+y)/2;
|
||||||
return sum(a,b,2*k,x,d) + sum(a,b,2*k+1,d+1,y);
|
return sum(a,b,2*k,x,d) + sum(a,b,2*k+1,d+1,y);
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Now we can calculate the sum of
|
Now we can calculate any value of $\texttt{sum}_q(a,b)$
|
||||||
elements in $[a,b]$ as follows:
|
(the sum of array values in range $[a,b]$) as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int s = sum(a, b, 1, 0, N-1);
|
int s = sum(a, b, 1, 0, n-1);
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
The parameter $k$ indicates the current position
|
The parameter $k$ indicates the current position
|
||||||
in \texttt{p}.
|
in \texttt{tree}.
|
||||||
Initially $k$ equals 1, because we begin
|
Initially $k$ equals 1, because we begin
|
||||||
at the root of the segment tree.
|
at the root of the tree.
|
||||||
The range $[x,y]$ corresponds to $k$
|
The range $[x,y]$ corresponds to $k$
|
||||||
and is initially $[0,N-1]$.
|
and is initially $[0,n-1]$.
|
||||||
When calculating the sum,
|
When calculating the sum,
|
||||||
if $[x,y]$ is outside $[a,b]$,
|
if $[x,y]$ is outside $[a,b]$,
|
||||||
the sum is 0,
|
the sum is 0,
|
||||||
and if $[x,y]$ is completely inside $[a,b]$,
|
and if $[x,y]$ is completely inside $[a,b]$,
|
||||||
the sum can be found in \texttt{p}.
|
the sum can be found in \texttt{tree}.
|
||||||
If $[x,y]$ is partially inside $[a,b]$,
|
If $[x,y]$ is partially inside $[a,b]$,
|
||||||
the search continues recursively to the
|
the search continues recursively to the
|
||||||
left and right half of $[x,y]$.
|
left and right half of $[x,y]$.
|
||||||
|
@ -67,9 +66,9 @@ and the right half is $[d+1,y]$
|
||||||
where $d=\lfloor \frac{x+y}{2} \rfloor$.
|
where $d=\lfloor \frac{x+y}{2} \rfloor$.
|
||||||
|
|
||||||
The following picture shows how the search proceeds
|
The following picture shows how the search proceeds
|
||||||
when calculating the sum of elements in $[a,b]$.
|
when calculating the value of $\texttt{sum}_q(a,b)$.
|
||||||
The gray nodes indicate nodes where the recursion
|
The gray nodes indicate nodes where the recursion
|
||||||
stops and the sum can be found in \texttt{p}.
|
stops and the sum can be found in \texttt{tree}.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -191,7 +190,7 @@ related to lazy updates, which has not been
|
||||||
propagated to its children.
|
propagated to its children.
|
||||||
|
|
||||||
There are two types of range updates:
|
There are two types of range updates:
|
||||||
each element in the range is either
|
each array value in the range is either
|
||||||
\emph{increased} by some value
|
\emph{increased} by some value
|
||||||
or \emph{assigned} some value.
|
or \emph{assigned} some value.
|
||||||
Both operations can be implemented using
|
Both operations can be implemented using
|
||||||
|
@ -202,17 +201,17 @@ a tree that supports both operations at the same time.
|
||||||
|
|
||||||
Let us consider an example where our goal is to
|
Let us consider an example where our goal is to
|
||||||
construct a segment tree that supports
|
construct a segment tree that supports
|
||||||
two operations: increasing each element in
|
two operations: increasing each value in
|
||||||
$[a,b]$ by a constant and calculating the sum of
|
$[a,b]$ by a constant and calculating the sum of
|
||||||
elements in $[a,b]$.
|
values in $[a,b]$.
|
||||||
|
|
||||||
We will construct a tree where each node
|
We will construct a tree where each node
|
||||||
contains two values $s/z$:
|
has two values $s/z$:
|
||||||
$s$ denotes the sum of elements in the range
|
$s$ denotes the sum of values in the range,
|
||||||
and $z$ denotes the value of a lazy update,
|
and $z$ denotes the value of a lazy update,
|
||||||
which means that all elements in the range
|
which means that all values in the range
|
||||||
should be increased by $z$.
|
should be increased by $z$.
|
||||||
In the following tree, $z=0$ for all nodes,
|
In the following tree, $z=0$ in all nodes,
|
||||||
so there are no ongoing lazy updates.
|
so there are no ongoing lazy updates.
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -290,7 +289,7 @@ When the elements in $[a,b]$ are increased by $u$,
|
||||||
we walk from the root towards the leaves
|
we walk from the root towards the leaves
|
||||||
and modify the nodes of the tree as follows:
|
and modify the nodes of the tree as follows:
|
||||||
If the range $[x,y]$ of a node is
|
If the range $[x,y]$ of a node is
|
||||||
completely inside the range $[a,b]$,
|
completely inside $[a,b]$,
|
||||||
we increase the $z$ value of the node by $u$ and stop.
|
we increase the $z$ value of the node by $u$ and stop.
|
||||||
If $[x,y]$ only partially belongs to $[a,b]$,
|
If $[x,y]$ only partially belongs to $[a,b]$,
|
||||||
we increase the $s$ value of the node by $hu$,
|
we increase the $s$ value of the node by $hu$,
|
||||||
|
@ -406,10 +405,9 @@ downwards only when it is necessary,
|
||||||
which guarantees that the operations are always efficient.
|
which guarantees that the operations are always efficient.
|
||||||
|
|
||||||
The following picture shows how the tree changes
|
The following picture shows how the tree changes
|
||||||
when we calculate the sum of elements in $[a,b]$.
|
when we calculate the value of $\texttt{sum}_a(a,b)$.
|
||||||
The rectangle shows the nodes whose values change,
|
The rectangle shows the nodes whose values change,
|
||||||
because a lazy update is propagated downwards,
|
because a lazy update is propagated downwards.
|
||||||
which is necessary to calculate the sum in $[a,b]$.
|
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -501,7 +499,7 @@ which is necessary to calculate the sum in $[a,b]$.
|
||||||
Note that sometimes it is needed to combine lazy updates.
|
Note that sometimes it is needed to combine lazy updates.
|
||||||
This happens when a node that already has a lazy update
|
This happens when a node that already has a lazy update
|
||||||
is assigned another lazy update.
|
is assigned another lazy update.
|
||||||
In this problem, it is easy to combine lazy updates,
|
When calculating sums, it is easy to combine lazy updates,
|
||||||
because the combination of updates $z_1$ and $z_2$
|
because the combination of updates $z_1$ and $z_2$
|
||||||
corresponds to an update $z_1+z_2$.
|
corresponds to an update $z_1+z_2$.
|
||||||
|
|
||||||
|
@ -511,13 +509,12 @@ Lazy updates can be generalized so that it is
|
||||||
possible to update ranges using polynomials of the form
|
possible to update ranges using polynomials of the form
|
||||||
\[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\]
|
\[p(u) = t_k u^k + t_{k-1} u^{k-1} + \cdots + t_0.\]
|
||||||
|
|
||||||
In this case, the update for an element
|
In this case, the update for a value
|
||||||
at position $i$
|
at position $i$ in $[a,b]$ is $p(i-a)$.
|
||||||
in the range $[a,b]$ is $p(i-a)$.
|
|
||||||
For example, adding the polynomial $p(u)=u+1$
|
For example, adding the polynomial $p(u)=u+1$
|
||||||
to the range $[a,b]$ means that the element at position $a$
|
to $[a,b]$ means that the value at position $a$
|
||||||
is increased by 1, the element at position $a+1$
|
increases by 1, the value at position $a+1$
|
||||||
is increased by 2, and so on.
|
increases by 2, and so on.
|
||||||
|
|
||||||
To support polynomial updates,
|
To support polynomial updates,
|
||||||
each node is assigned $k+2$ values,
|
each node is assigned $k+2$ values,
|
||||||
|
@ -526,7 +523,7 @@ The value $s$ is the sum of the elements in the range,
|
||||||
and the values $z_0,z_1,\ldots,z_k$ are the coefficients
|
and the values $z_0,z_1,\ldots,z_k$ are the coefficients
|
||||||
of a polynomial that corresponds to a lazy update.
|
of a polynomial that corresponds to a lazy update.
|
||||||
|
|
||||||
Now, the sum of elements in a range $[x,y]$ equals
|
Now, the sum of values in a range $[x,y]$ equals
|
||||||
\[s+\sum_{u=0}^{y-x} z_k u^k + z_{k-1} u^{k-1} + \cdots + z_0.\]
|
\[s+\sum_{u=0}^{y-x} z_k u^k + z_{k-1} u^{k-1} + \cdots + z_0.\]
|
||||||
|
|
||||||
The value of such a sum
|
The value of such a sum
|
||||||
|
@ -560,27 +557,25 @@ are actually accessed during the algorithm,
|
||||||
which can save a large amount of memory.
|
which can save a large amount of memory.
|
||||||
|
|
||||||
The nodes of a dynamic tree can be represented as structs:
|
The nodes of a dynamic tree can be represented as structs:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
struct node {
|
struct node {
|
||||||
int v;
|
int value;
|
||||||
int x, y;
|
int x, y;
|
||||||
node *l, *r;
|
node *left, *right;
|
||||||
node(int v, int x, int y) : v(v), x(x), y(y) {}
|
node(int v, int x, int y) : value(v), x(x), y(y) {}
|
||||||
};
|
};
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
Here $v$ is the value of the node,
|
Here \texttt{value} is the value of the node,
|
||||||
$[x,y]$ is the corresponding range,
|
$[\texttt{x},\texttt{y}]$ is the corresponding range,
|
||||||
and $l$ and $r$ point to the left
|
and \texttt{left} and \texttt{right} point to the left
|
||||||
and right subtree.
|
and right subtree.
|
||||||
|
|
||||||
After this, nodes can be created as follows:
|
After this, nodes can be created as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
// create new node
|
// create new node
|
||||||
node *u = new node(0, 0, 15);
|
node *x = new node(0, 0, 15);
|
||||||
// change value
|
// change value
|
||||||
u->v = 5;
|
x->value = 5;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\subsubsection{Sparse segment trees}
|
\subsubsection{Sparse segment trees}
|
||||||
|
@ -589,19 +584,19 @@ u->v = 5;
|
||||||
|
|
||||||
A dynamic segment tree is useful when
|
A dynamic segment tree is useful when
|
||||||
the underlying array is \emph{sparse},
|
the underlying array is \emph{sparse},
|
||||||
i.e., the range $[0,N-1]$
|
i.e., the range $[0,n-1]$
|
||||||
of allowed indices is large,
|
of allowed indices is large,
|
||||||
but most array values are zeros.
|
but most array values are zeros.
|
||||||
While an ordinary segment tree uses $O(N)$ memory,
|
While an ordinary segment tree uses $O(n)$ memory,
|
||||||
a dynamic segment tree only uses $O(n \log N)$ memory,
|
a dynamic segment tree only uses $O(k \log n)$ memory,
|
||||||
where $n$ is the number of operations performed.
|
where $k$ is the number of operations performed.
|
||||||
|
|
||||||
A \key{sparse segment tree} initially has
|
A \key{sparse segment tree} initially has
|
||||||
only one node $[0,N-1]$ whose value is zero,
|
only one node $[0,n-1]$ whose value is zero,
|
||||||
which means that every array value is zero.
|
which means that every array value is zero.
|
||||||
After updates, new nodes are dynamically added
|
After updates, new nodes are dynamically added
|
||||||
to the tree.
|
to the tree.
|
||||||
For example, if $N=16$ and the elements
|
For example, if $n=16$ and the elements
|
||||||
in positions 3 and 10 have been modified,
|
in positions 3 and 10 have been modified,
|
||||||
the tree contains the following nodes:
|
the tree contains the following nodes:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
|
@ -630,16 +625,16 @@ the tree contains the following nodes:
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Any path from the root node to a leaf contains
|
Any path from the root node to a leaf contains
|
||||||
$O(\log N)$ nodes,
|
$O(\log n)$ nodes,
|
||||||
so each operation adds at most $O(\log N)$
|
so each operation adds at most $O(\log n)$
|
||||||
new nodes to the tree.
|
new nodes to the tree.
|
||||||
Thus, after $n$ operations, the tree contains
|
Thus, after $k$ operations, the tree contains
|
||||||
at most $O(n \log N)$ nodes.
|
at most $O(k \log n)$ nodes.
|
||||||
|
|
||||||
Note that if all indices of the elements
|
Note that if we know all elements to be updated
|
||||||
are known at the beginning of the algorithm,
|
at the beginning of the algorithm,
|
||||||
a dynamic segment tree is not necessary,
|
a dynamic segment tree is not necessary,
|
||||||
but we can use an ordinary segment tree with
|
because we can use an ordinary segment tree with
|
||||||
index compression (Chapter 9.4).
|
index compression (Chapter 9.4).
|
||||||
However, this is not possible when the indices
|
However, this is not possible when the indices
|
||||||
are generated during the algorithm.
|
are generated during the algorithm.
|
||||||
|
@ -773,8 +768,8 @@ stored as follows:
|
||||||
The structure of each previous tree can be
|
The structure of each previous tree can be
|
||||||
reconstructed by following the pointers
|
reconstructed by following the pointers
|
||||||
starting at the corresponding root node.
|
starting at the corresponding root node.
|
||||||
Since each operation during the algorithm
|
Since each operation
|
||||||
adds only $O(\log N)$ new nodes to the tree,
|
adds only $O(\log n)$ new nodes to the tree,
|
||||||
it is possible to store the full modification history of the tree.
|
it is possible to store the full modification history of the tree.
|
||||||
|
|
||||||
\section{Data structures}
|
\section{Data structures}
|
||||||
|
|
Loading…
Reference in New Issue