Improvements for graph algorithms
This commit is contained in:
parent
086d0e61cd
commit
31ff123f33
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue