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

View File

@ -204,18 +204,19 @@ The algorithm consists of $n-1$ rounds,
and on each round the algorithm goes through
all edges of the graph and tries to
reduce the distances.
The algorithm constructs an array \texttt{e}
The algorithm constructs an array \texttt{distance}
that will contain the distance from $x$
to all nodes in the graph.
The initial value $10^9$ means infinity.
\begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9;
e[x] = 0;
for (int i = 1; i <= n; i++) distance[i] = 1e9;
distance[x] = 0;
for (int i = 1; i <= n-1; i++) {
for (int a = 1; a <= n; 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
can be reduced during a round.
\subsubsection{Negative cycle}
\subsubsection{Negative cycles}
\index{negative cycle}
@ -300,22 +301,22 @@ node $b$ is added to the queue.
The following implementation uses a
\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,
in which case the algorithm does not add
the node to the queue again.
\begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9;
e[x] = 0;
for (int i = 1; i <= n; i++) distance[i] = 1e9;
distance[x] = 0;
q.push(x);
while (!q.empty()) {
int a = q.front(); q.pop();
z[a] = 0;
inqueue[a] = false;
for (auto b : v[a]) {
if (e[a]+b.second < e[b.first]) {
e[b.first] = e[a]+b.second;
if (!z[b]) {q.push(b); z[b] = 1;}
if (distance[a]+b.second < distance[b.first]) {
distance[b.first] = distance[a]+b.second;
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,
we should find the node with the \emph{minimum} distance,
while the C++ priority queue finds the \emph{maximum}
element as default.
element by default.
An easy trick is to use \emph{negative} distances,
which allows us to directly use the C++ priority queue.
The code keeps track of processed nodes
in an array \texttt{z},
and maintains the distances in an array \texttt{e}.
in an array \texttt{ready},
and maintains the distances in an array \texttt{distance}.
Initially, the distance to the starting node is 0,
and the distance to all other nodes is $10^9$ (infinite).
\begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9;
e[x] = 0;
for (int i = 1; i <= n; i++) distance[i] = 1e9;
distance[x] = 0;
q.push({0,x});
while (!q.empty()) {
int a = q.top().second; q.pop();
if (z[a]) continue;
z[a] = 1;
if (ready[a]) continue;
ready[a] = true;
for (auto b : v[a]) {
if (e[a]+b.second < e[b.first]) {
e[b.first] = e[a]+b.second;
q.push({-e[b.first],b.first});
if (distance[a]+b.second < distance[b.first]) {
distance[b.first] = distance[a]+b.second;
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.
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
all nodes in the subtrees of its children.
Thus, we can calculate the number of nodes
@ -144,11 +144,11 @@ recursively using the following code:
\begin{lstlisting}
void dfs(int s, int e) {
c[s] = 1;
count[s] = 1;
for (auto u : v[s]) {
if (u == e) continue;
dfs(u, s);
c[s] += c[u];
count[s] += count[u];
}
}
\end{lstlisting}

View File

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