Improvements for graph algorithms

This commit is contained in:
Antti H S Laaksonen 2017-04-17 13:58:04 +03:00
parent 086d0e61cd
commit 31ff123f33
4 changed files with 58 additions and 56 deletions

View File

@ -133,17 +133,17 @@ vector<int> v[N];
\end{lstlisting} \end{lstlisting}
and also maintains an array and also maintains an array
\begin{lstlisting} \begin{lstlisting}
bool z[N]; bool visited[N];
\end{lstlisting} \end{lstlisting}
that keeps track of the visited nodes. that keeps track of the visited nodes.
Initially, each array value is 0, Initially, each array value is \texttt{false},
and when the search arrives at node $s$, and when the search arrives at node $s$,
the value of \texttt{z}[$s$] becomes 1. the value of \texttt{visited}[$s$] becomes \texttt{true}.
The function can be implemented as follows: The function can be implemented as follows:
\begin{lstlisting} \begin{lstlisting}
void dfs(int s) { void dfs(int s) {
if (z[s]) return; if (visited[s]) return;
z[s] = 1; visited[s] = true;
// process node s // process node s
for (auto u: v[s]) { for (auto u: v[s]) {
dfs(u); dfs(u);
@ -308,38 +308,39 @@ a queue that contains nodes.
At each step, the next node in the queue At each step, the next node in the queue
will be processed. will be processed.
The following code begins a breadth-first The following code assumes that the graph is stored
search at node $x$. as adjacency lists and maintains the following
The code assumes that the graph is stored data structures:
as adjacency lists and maintains a queue
\begin{lstlisting} \begin{lstlisting}
queue<int> q; queue<int> q;
bool visited[N];
int distance[N];
\end{lstlisting} \end{lstlisting}
that contains the nodes in increasing order
The queue \texttt{q}
contains the nodes in increasing order
of their distance. of their distance.
New nodes are always added to the end New nodes are always added to the end
of the queue, and the node at the beginning of the queue, and the node at the beginning
of the queue is the next node to be processed. of the queue is the next node to be processed.
The array \texttt{visited} indicates
In addition, the code uses arrays which nodes the search has already visited,
\begin{lstlisting} and the array \texttt{distance} will contain the
bool z[N];
int e[N];
\end{lstlisting}
so that the array \texttt{z} indicates
which nodes the search has already visited
and the array \texttt{e} will contain the
distances to all nodes in the graph. distances to all nodes in the graph.
The search can be implemented as follows:
The search can be implemented as follows,
starting at node $x$:
\begin{lstlisting} \begin{lstlisting}
z[x] = 1; e[x] = 0; visited[x] = true;
distance[x] = 0;
q.push(x); q.push(x);
while (!q.empty()) { while (!q.empty()) {
int s = q.front(); q.pop(); int s = q.front(); q.pop();
// process node s // process node s
for (auto u : v[s]) { for (auto u : v[s]) {
if (z[u]) continue; if (visited[u]) continue;
z[u] = 1; e[u] = e[s]+1; visited[u] = true;
distance[u] = distance[s]+1;
q.push(u); q.push(u);
} }
} }

View File

@ -204,18 +204,19 @@ The algorithm consists of $n-1$ rounds,
and on each round the algorithm goes through and on each round the algorithm goes through
all edges of the graph and tries to all edges of the graph and tries to
reduce the distances. reduce the distances.
The algorithm constructs an array \texttt{e} The algorithm constructs an array \texttt{distance}
that will contain the distance from $x$ that will contain the distance from $x$
to all nodes in the graph. to all nodes in the graph.
The initial value $10^9$ means infinity. The initial value $10^9$ means infinity.
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9; for (int i = 1; i <= n; i++) distance[i] = 1e9;
e[x] = 0; distance[x] = 0;
for (int i = 1; i <= n-1; i++) { for (int i = 1; i <= n-1; i++) {
for (int a = 1; a <= n; a++) { for (int a = 1; a <= n; a++) {
for (auto b : v[a]) { for (auto b : v[a]) {
e[b.first] = min(e[b.first],e[a]+b.second); distance[b.first] = min(distance[b.first],
distance[a]+b.second);
} }
} }
} }
@ -234,7 +235,7 @@ Thus, a possible way to make the algorithm more efficient
is to stop the algorithm if no distance is to stop the algorithm if no distance
can be reduced during a round. can be reduced during a round.
\subsubsection{Negative cycle} \subsubsection{Negative cycles}
\index{negative cycle} \index{negative cycle}
@ -300,22 +301,22 @@ node $b$ is added to the queue.
The following implementation uses a The following implementation uses a
\texttt{queue} \texttt{q}. \texttt{queue} \texttt{q}.
In addition, an array \texttt{z} indicates In addition, an array \texttt{inqueue} indicates
if a node is already in the queue, if a node is already in the queue,
in which case the algorithm does not add in which case the algorithm does not add
the node to the queue again. the node to the queue again.
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9; for (int i = 1; i <= n; i++) distance[i] = 1e9;
e[x] = 0; distance[x] = 0;
q.push(x); q.push(x);
while (!q.empty()) { while (!q.empty()) {
int a = q.front(); q.pop(); int a = q.front(); q.pop();
z[a] = 0; inqueue[a] = false;
for (auto b : v[a]) { for (auto b : v[a]) {
if (e[a]+b.second < e[b.first]) { if (distance[a]+b.second < distance[b.first]) {
e[b.first] = e[a]+b.second; distance[b.first] = distance[a]+b.second;
if (!z[b]) {q.push(b); z[b] = 1;} if (!inqueue[b]) {q.push(b); inqueue[b] = true;}
} }
} }
} }
@ -562,28 +563,28 @@ priority_queue<pair<int,int>> q;
A small difficulty is that in Dijkstra's algorithm, A small difficulty is that in Dijkstra's algorithm,
we should find the node with the \emph{minimum} distance, we should find the node with the \emph{minimum} distance,
while the C++ priority queue finds the \emph{maximum} while the C++ priority queue finds the \emph{maximum}
element as default. element by default.
An easy trick is to use \emph{negative} distances, An easy trick is to use \emph{negative} distances,
which allows us to directly use the C++ priority queue. which allows us to directly use the C++ priority queue.
The code keeps track of processed nodes The code keeps track of processed nodes
in an array \texttt{z}, in an array \texttt{ready},
and maintains the distances in an array \texttt{e}. and maintains the distances in an array \texttt{distance}.
Initially, the distance to the starting node is 0, Initially, the distance to the starting node is 0,
and the distance to all other nodes is $10^9$ (infinite). and the distance to all other nodes is $10^9$ (infinite).
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9; for (int i = 1; i <= n; i++) distance[i] = 1e9;
e[x] = 0; distance[x] = 0;
q.push({0,x}); q.push({0,x});
while (!q.empty()) { while (!q.empty()) {
int a = q.top().second; q.pop(); int a = q.top().second; q.pop();
if (z[a]) continue; if (ready[a]) continue;
z[a] = 1; ready[a] = true;
for (auto b : v[a]) { for (auto b : v[a]) {
if (e[a]+b.second < e[b.first]) { if (distance[a]+b.second < distance[b.first]) {
e[b.first] = e[a]+b.second; distance[b.first] = distance[a]+b.second;
q.push({-e[b.first],b.first}); q.push({-distance[b.first],b.first});
} }
} }
} }

View File

@ -136,7 +136,7 @@ or the length of the longest path from the node
to a leaf. to a leaf.
As an example, let us calculate for each node $s$ As an example, let us calculate for each node $s$
a value $\texttt{c}[s]$: the number of nodes in its subtree. a value $\texttt{count}[s]$: the number of nodes in its subtree.
The subtree contains the node itself and The subtree contains the node itself and
all nodes in the subtrees of its children. all nodes in the subtrees of its children.
Thus, we can calculate the number of nodes Thus, we can calculate the number of nodes
@ -144,11 +144,11 @@ recursively using the following code:
\begin{lstlisting} \begin{lstlisting}
void dfs(int s, int e) { void dfs(int s, int e) {
c[s] = 1; count[s] = 1;
for (auto u : v[s]) { for (auto u : v[s]) {
if (u == e) continue; if (u == e) continue;
dfs(u, s); dfs(u, s);
c[s] += c[u]; count[s] += count[u];
} }
} }
\end{lstlisting} \end{lstlisting}

View File

@ -504,17 +504,17 @@ efficiently by following the corresponding chain.
The union-find structure can be implemented The union-find structure can be implemented
using arrays. using arrays.
In the following implementation, In the following implementation,
the array \texttt{k} contains for each element the array \texttt{link} contains for each element
the next element the next element
in the chain or the element itself if it is in the chain or the element itself if it is
a representative, a representative,
and the array \texttt{s} indicates for each representative and the array \texttt{size} indicates for each representative
the size of the corresponding set. the size of the corresponding set.
Initially, each element belongs to a separate set: Initially, each element belongs to a separate set:
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) k[i] = i; for (int i = 1; i <= n; i++) link[i] = i;
for (int i = 1; i <= n; i++) s[i] = 1; for (int i = 1; i <= n; i++) size[i] = 1;
\end{lstlisting} \end{lstlisting}
The function \texttt{find} returns The function \texttt{find} returns
@ -524,7 +524,7 @@ the chain that begins at $x$.
\begin{lstlisting} \begin{lstlisting}
int find(int x) { int find(int x) {
while (x != k[x]) x = k[x]; while (x != link[x]) x = link[x];
return x; return x;
} }
\end{lstlisting} \end{lstlisting}
@ -552,9 +552,9 @@ set to the larger set.
void unite(int a, int b) { void unite(int a, int b) {
a = find(a); a = find(a);
b = find(b); b = find(b);
if (s[a] < s[b]) swap(a,b); if (size[a] < size[b]) swap(a,b);
s[a] += s[b]; size[a] += size[b];
k[b] = a; link[b] = a;
} }
\end{lstlisting} \end{lstlisting}
\end{samepage} \end{samepage}