Better names for variables [closes #18]

This commit is contained in:
Antti H S Laaksonen 2017-04-17 15:27:43 +03:00
parent baee83266a
commit 11564fd4d3
4 changed files with 115 additions and 118 deletions

View File

@ -480,7 +480,7 @@ efficiently implemented using them.
A convenient way to store the adjacency lists is to declare A convenient way to store the adjacency lists is to declare
an array of vectors as follows: an array of vectors as follows:
\begin{lstlisting} \begin{lstlisting}
vector<int> v[N]; vector<int> adj[N];
\end{lstlisting} \end{lstlisting}
The constant $N$ is chosen so that all The constant $N$ is chosen so that all
@ -503,11 +503,11 @@ For example, the graph
\end{center} \end{center}
can be stored as follows: can be stored as follows:
\begin{lstlisting} \begin{lstlisting}
v[1].push_back(2); adj[1].push_back(2);
v[2].push_back(3); adj[2].push_back(3);
v[2].push_back(4); adj[2].push_back(4);
v[3].push_back(4); adj[3].push_back(4);
v[4].push_back(1); adj[4].push_back(1);
\end{lstlisting} \end{lstlisting}
If the graph is undirected, it can be stored in a similar way, If the graph is undirected, it can be stored in a similar way,
@ -517,7 +517,7 @@ For a weighted graph, the structure can be extended
as follows: as follows:
\begin{lstlisting} \begin{lstlisting}
vector<pair<int,int>> v[N]; vector<pair<int,int>> adj[N];
\end{lstlisting} \end{lstlisting}
If there is an edge from node $a$ to node $b$ If there is an edge from node $a$ to node $b$
@ -541,11 +541,11 @@ For example, the graph
\end{center} \end{center}
can be stored as follows: can be stored as follows:
\begin{lstlisting} \begin{lstlisting}
v[1].push_back({2,5}); adj[1].push_back({2,5});
v[2].push_back({3,7}); adj[2].push_back({3,7});
v[2].push_back({4,6}); adj[2].push_back({4,6});
v[3].push_back({4,5}); adj[3].push_back({4,5});
v[4].push_back({1,2}); adj[4].push_back({1,2});
\end{lstlisting} \end{lstlisting}
The benefit in using adjacency lists is that The benefit in using adjacency lists is that
@ -555,7 +555,7 @@ For example, the following loop goes through all nodes
to which we can move from node $s$: to which we can move from node $s$:
\begin{lstlisting} \begin{lstlisting}
for (auto u : v[s]) { for (auto u : adj[s]) {
// process node u // process node u
} }
\end{lstlisting} \end{lstlisting}
@ -570,14 +570,14 @@ We can efficiently check from an adjacency matrix
if there is an edge between two nodes. if there is an edge between two nodes.
The matrix can be stored as an array The matrix can be stored as an array
\begin{lstlisting} \begin{lstlisting}
int v[N][N]; int mat[N][N];
\end{lstlisting} \end{lstlisting}
where each value $\texttt{v}[a][b]$ indicates where each value $\texttt{mat}[a][b]$ indicates
whether the graph contains an edge from whether the graph contains an edge from
node $a$ to node $b$. node $a$ to node $b$.
If the edge is included in the graph, If the edge is included in the graph,
then $\texttt{v}[a][b]=1$, then $\texttt{mat}[a][b]=1$,
and otherwise $\texttt{v}[a][b]=0$. and otherwise $\texttt{mat}[a][b]=0$.
For example, the graph For example, the graph
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -696,7 +696,7 @@ at a given node.
The edge list can be stored in a vector The edge list can be stored in a vector
\begin{lstlisting} \begin{lstlisting}
vector<pair<int,int>> v; vector<pair<int,int>> edges;
\end{lstlisting} \end{lstlisting}
where each pair $(a,b)$ denotes that where each pair $(a,b)$ denotes that
there is an edge from node $a$ to node $b$. there is an edge from node $a$ to node $b$.
@ -718,18 +718,18 @@ Thus, the graph
\end{center} \end{center}
can be represented as follows: can be represented as follows:
\begin{lstlisting} \begin{lstlisting}
v.push_back({1,2}); edges.push_back({1,2});
v.push_back({2,3}); edges.push_back({2,3});
v.push_back({2,4}); edges.push_back({2,4});
v.push_back({3,4}); edges.push_back({3,4});
v.push_back({4,1}); edges.push_back({4,1});
\end{lstlisting} \end{lstlisting}
\noindent \noindent
If the graph is weighted, the structure can If the graph is weighted, the structure can
be extended as follows: be extended as follows:
\begin{lstlisting} \begin{lstlisting}
vector<tuple<int,int,int>> v; vector<tuple<int,int,int>> edges;
\end{lstlisting} \end{lstlisting}
Each element in this list is of the Each element in this list is of the
form $(a,b,w)$, which means that there form $(a,b,w)$, which means that there
@ -753,10 +753,10 @@ For example, the graph
\begin{samepage} \begin{samepage}
can be represented as follows: can be represented as follows:
\begin{lstlisting} \begin{lstlisting}
v.push_back(make_tuple(1,2,5)); edges.push_back({1,2,5});
v.push_back(make_tuple(2,3,7)); edges.push_back({2,3,7});
v.push_back(make_tuple(2,4,6)); edges.push_back({2,4,6});
v.push_back(make_tuple(3,4,5)); edges.push_back({3,4,5});
v.push_back(make_tuple(4,1,2)); edges.push_back({4,1,2});
\end{lstlisting} \end{lstlisting}
\end{samepage} \end{samepage}

View File

@ -129,23 +129,23 @@ a depth-first search at a given node.
The function assumes that the graph is The function assumes that the graph is
stored as adjacency lists in an array stored as adjacency lists in an array
\begin{lstlisting} \begin{lstlisting}
vector<int> v[N]; vector<int> adj[N];
\end{lstlisting} \end{lstlisting}
and also maintains an array and also maintains an array
\begin{lstlisting} \begin{lstlisting}
bool visited[N]; bool vis[N];
\end{lstlisting} \end{lstlisting}
that keeps track of the visited nodes. that keeps track of the visited nodes.
Initially, each array value is \texttt{false}, 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{visited}[$s$] becomes \texttt{true}. the value of \texttt{vis}[$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 (visited[s]) return; if (vis[s]) return;
visited[s] = true; vis[s] = true;
// process node s // process node s
for (auto u: v[s]) { for (auto u: adj[s]) {
dfs(u); dfs(u);
} }
} }
@ -313,8 +313,8 @@ as adjacency lists and maintains the following
data structures: data structures:
\begin{lstlisting} \begin{lstlisting}
queue<int> q; queue<int> q;
bool visited[N]; bool vis[N];
int distance[N]; int dist[N];
\end{lstlisting} \end{lstlisting}
The queue \texttt{q} The queue \texttt{q}
@ -323,24 +323,24 @@ 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 The array \texttt{vis} indicates
which nodes the search has already visited, which nodes the search has already visited,
and the array \texttt{distance} will contain the and the array \texttt{dist} 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$: starting at node $x$:
\begin{lstlisting} \begin{lstlisting}
visited[x] = true; vis[x] = true;
distance[x] = 0; dist[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 : adj[s]) {
if (visited[u]) continue; if (vis[u]) continue;
visited[u] = true; vis[u] = true;
distance[u] = distance[s]+1; dist[u] = dist[s]+1;
q.push(u); q.push(u);
} }
} }

View File

@ -193,12 +193,10 @@ The following implementation of the
BellmanFord algorithm finds the shortest distances BellmanFord algorithm finds the shortest distances
from a node $x$ to all other nodes in the graph. from a node $x$ to all other nodes in the graph.
The code assumes that the graph is stored The code assumes that the graph is stored
as adjacency lists in an array as an edge list \texttt{edges}
\begin{lstlisting} that consists of tuples of the form $(a,b,w)$,
vector<pair<int,int>> v[N]; meaning that there is an edge from node $a$ to node $b$
\end{lstlisting} with weight $w$.
as pairs of the form $(x,w)$:
there is an edge to node $x$ with weight $w$.
The algorithm consists of $n-1$ rounds, The algorithm consists of $n-1$ rounds,
and on each round the algorithm goes through and on each round the algorithm goes through
@ -210,14 +208,13 @@ to all nodes in the graph.
The constant \texttt{INF} denotes an infinite distance. The constant \texttt{INF} denotes an infinite distance.
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) distance[i] = INF; for (int i = 1; i <= n; i++) dist[i] = INF;
distance[x] = 0; dist[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 (auto e : edges) {
for (auto b : v[a]) { int a, b, w;
distance[b.first] = min(distance[b.first], tie(a, b, w) = e;
distance[a]+b.second); dist[b] = min(dist[b], dist[a]+w);
}
} }
} }
\end{lstlisting} \end{lstlisting}
@ -298,29 +295,29 @@ Then, the algorithm always processes the
first node in the queue, and when an edge first node in the queue, and when an edge
$a \rightarrow b$ reduces a distance, $a \rightarrow b$ reduces a distance,
node $b$ is added to the queue. 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{inqueue} 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++) distance[i] = INF; % for (int i = 1; i <= n; i++) distance[i] = INF;
distance[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();
inqueue[a] = false; % inqueue[a] = false;
for (auto b : v[a]) { % for (auto b : v[a]) {
if (distance[a]+b.second < distance[b.first]) { % if (distance[a]+b.second < distance[b.first]) {
distance[b.first] = distance[a]+b.second; % distance[b.first] = distance[a]+b.second;
if (!inqueue[b]) {q.push(b); inqueue[b] = true;} % if (!inqueue[b]) {q.push(b); inqueue[b] = true;}
} % }
} % }
} % }
\end{lstlisting} % \end{lstlisting}
The efficiency of the SPFA algorithm depends The efficiency of the SPFA algorithm depends
on the structure of the graph: on the structure of the graph:
@ -542,8 +539,10 @@ compensates the previous large weight $6$.
The following implementation of Dijkstra's algorithm The following implementation of Dijkstra's algorithm
calculates the minimum distances from a node $x$ calculates the minimum distances from a node $x$
to all other nodes. to all other nodes.
The graph is stored in an array \texttt{v} The graph is stored as adjacency lists
as adjacency lists like in the BellmanFord algorithm. so that \texttt{adj[$a$]} contains a pair $(b,w)$
always when there is an edge from node $a$ to node $b$
with weight $w$.
An efficient implementation of Dijkstra's algorithm An efficient implementation of Dijkstra's algorithm
requires that it is possible to efficiently find the requires that it is possible to efficiently find the
@ -553,43 +552,41 @@ that contains the nodes ordered by their distances.
Using a priority queue, the next node to be processed Using a priority queue, the next node to be processed
can be retrieved in logarithmic time. can be retrieved in logarithmic time.
In the following implementation, The following implementation uses a priority queue
the priority queue contains pairs whose first \texttt{q} that contains pairs of the form $(-d,x)$:
element is the current distance to the node and second the current distance to node $x$ is $d$.
element is the identifier of the node. The array $\texttt{dist}$ contains the distance to
\begin{lstlisting} each node, and the array $\texttt{ready}$ indicates
priority_queue<pair<int,int>> q; whether a node has been processed.
\end{lstlisting} Initially the distance to $0$ to $x$ and $\infty$ to all other nodes.
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 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{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 infinite.
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) distance[i] = INF; for (int i = 1; i <= n; i++) dist[i] = INF;
distance[x] = 0; dist[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 (ready[a]) continue; if (ready[a]) continue;
ready[a] = true; ready[a] = true;
for (auto b : v[a]) { for (auto u : v[a]) {
if (distance[a]+b.second < distance[b.first]) { int b = u.first, w = u.second;
distance[b.first] = distance[a]+b.second; if (dist[a]+w < dist[b]) {
q.push({-distance[b.first],b.first}); dist[b] = dist[a]+w;
q.push({-dist[b],b});
} }
} }
} }
\end{lstlisting} \end{lstlisting}
Note that the priority queue contains \emph{negative}
distances to nodes.
The reason for this is that the C++ priority queue finds the \emph{maximum}
element by default while we would like to find \emph{minimum} elements.
By using negative distances,
we can directly use the default version of the C++ priority queue\footnote{Of
course, we could also declare the priority queue as in Chapter 4.5
and use positive distances, but the implementation would be a bit longer.}.
The time complexity of the above implementation is The time complexity of the above implementation is
$O(n+m \log m)$ because the algorithm goes through $O(n+m \log m)$ because the algorithm goes through
all nodes in the graph and adds for each edge all nodes in the graph and adds for each edge
@ -761,17 +758,17 @@ The advantage of the
FloydWarshall algorithm that it is FloydWarshall algorithm that it is
easy to implement. easy to implement.
The following code constructs a The following code constructs a
distance matrix \texttt{d} where $\texttt{d}[a][b]$ distance matrix where $\texttt{dist}[a][b]$
is the shortest distance between nodes $a$ and $b$. is the shortest distance between nodes $a$ and $b$.
First, the algorithm initializes \texttt{d} First, the algorithm initializes \texttt{dist}
using the adjacency matrix \texttt{v} of the graph: using the adjacency matrix \texttt{mat} of the graph:
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) { for (int j = 1; j <= n; j++) {
if (i == j) d[i][j] = 0; if (i == j) dist[i][j] = 0;
else if (v[i][j]) d[i][j] = v[i][j]; else if (mat[i][j]) dist[i][j] = mat[i][j];
else d[i][j] = INF; else dist[i][j] = INF;
} }
} }
\end{lstlisting} \end{lstlisting}
@ -782,7 +779,7 @@ After this, the shortest distances can be found as follows:
for (int k = 1; k <= n; k++) { for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) { for (int j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][k]+d[k][j]); dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]);
} }
} }
} }
@ -790,7 +787,7 @@ for (int k = 1; k <= n; k++) {
The time complexity of the algorithm is $O(n^3)$, The time complexity of the algorithm is $O(n^3)$,
because it contains three nested loops because it contains three nested loops
that go through the nodes in the graph. that go through the nodes of the graph.
Since the implementation of the FloydWarshall Since the implementation of the FloydWarshall
algorithm is simple, the algorithm can be algorithm is simple, the algorithm can be

View File

@ -101,7 +101,7 @@ The following recursive function can be used:
\begin{lstlisting} \begin{lstlisting}
void dfs(int s, int e) { void dfs(int s, int e) {
// process node s // process node s
for (auto u : v[s]) { for (auto u : adj[s]) {
if (u != e) dfs(u, s); if (u != e) dfs(u, s);
} }
} }
@ -145,7 +145,7 @@ recursively using the following code:
\begin{lstlisting} \begin{lstlisting}
void dfs(int s, int e) { void dfs(int s, int e) {
count[s] = 1; count[s] = 1;
for (auto u : v[s]) { for (auto u : adj[s]) {
if (u == e) continue; if (u == e) continue;
dfs(u, s); dfs(u, s);
count[s] += count[u]; count[s] += count[u];