图相关算法

无向图

图:是由一组顶点和一组能够将两个顶点相连的边组成的。点: Vertex, 边:Edge;

路径: 是由边顺序连接的一系列顶点。

连通图:从任意一个顶点都存在一条路径到达另一个任意顶点,称为连通图。

树:是一副无环连通图。

当且仅当一幅含有 V 个结点的图 G 满足下列5个条件之一时,它就是一个树:

  • G 有 V- 1 条边且不含有环
  • G 有 V- 1条边且是连通的
  • G 是连通的, 但删除任意一条边都会使它不再连通
  • G 是无环图,但添加任意一条边都会产生一个环
  • G 中的任意一对顶点之间仅存在一条简单路径。

邻接表

定义:将每个顶点的所有相邻顶点都保存在该顶点对应的元素所指向的一张链表中。

特点:

  1. 使用的空间和 V + E 成正比
  2. 添加一条边所需的时间为常数
  3. 遍历顶点 V 的所有相邻顶点所需的时间和 V 的度数成正比;

图的 API 表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public class Graph {
private static final String NEWLINE = System.getProperty("line.separator");
private final int V; //顶点数目
private int E; //边的数目
private Bag<Integer>[] adj; //邻接表
/**
* Initializes an empty graph with {@code V} vertices and 0 edges.
* param V the number of vertices
*
* @param V number of vertices
* @throws IllegalArgumentException if {@code V < 0}
*/
public Graph(int V) {
if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
this.V = V;
this.E = 0;
adj = (Bag<Integer>[]) new Bag[V];
for (int v = 0; v < V; v++) {
adj[v] = new Bag<Integer>();
}
}
/**
* Initializes a new graph that is a deep copy of {@code G}.
*
* @param G the graph to copy
*/
public Graph(Graph G) {
this(G.V());
this.E = G.E();
for (int v = 0; v < G.V(); v++) {
// reverse so that adjacency list is in same order as original
Stack<Integer> reverse = new Stack<Integer>();
for (int w : G.adj[v]) {
reverse.push(w);
}
for (int w : reverse) {
adj[v].add(w);
}
}
}
/**
* Returns the number of vertices in this graph.
*
* @return the number of vertices in this graph
*/
public int V() {
return V;
}
/**
* Returns the number of edges in this graph.
*
* @return the number of edges in this graph
*/
public int E() {
return E;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Adds the undirected edge v-w to this graph.
*
* @param v one vertex in the edge
* @param w the other vertex in the edge
* @throws IllegalArgumentException unless both {@code 0 <= v < V} and {@code 0 <= w < V}
*/
public void addEdge(int v, int w) {
validateVertex(v);
validateVertex(w);
E++;
adj[v].add(w);
adj[w].add(v);
}
/**
* Returns the vertices adjacent to vertex {@code v}.
*
* @param v the vertex
* @return the vertices adjacent to vertex {@code v}, as an iterable
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Integer> adj(int v) {
validateVertex(v);
return adj[v];
}
/**
* Returns the degree of vertex {@code v}.
*
* @param v the vertex
* @return the degree of vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int degree(int v) {
validateVertex(v);
return adj[v].size();
}
/**
* Returns a string representation of this graph.
*
* @return the number of vertices <em>V</em>, followed by the number of edges <em>E</em>,
* followed by the <em>V</em> adjacency lists
*/
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " vertices, " + E + " edges " + NEWLINE);
for (int v = 0; v < V; v++) {
s.append(v + ": ");
for (int w : adj[v]) {
s.append(w + " ");
}
s.append(NEWLINE);
}
return s.toString();
}
/**
* Unit tests the {@code Graph} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
Graph G = new Graph(in);
StdOut.println(G);
}
}

深度优先搜索— DFS

要搜索一副图,只需用一个递归方法来遍历所有的顶点,在访问其中一个顶点时:

  • 将它标记为已访问;
  • 递归地访问它的所有没有被标记过的邻居顶点。

解决问题:

  1. 连通性,两个顶点是否连通?
  2. 单点路径问题,从 s 到给定目的顶点v 是否存在一条路径?

寻找路径

使用深度优先搜索查找图中的路径

DepthFirstPaths.java

其中 edge[] 记录着每个顶点到起点的路径,而不是记录当前顶点到起点的路径;例如:在由边 v - w 第一次访问任意 w 时,将 edge[w] = v 来记录这条路径。换句话说,v - w 是从 s 到 w 的路径上的最后一条已知边;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public class DepthFirstPaths {
private boolean[] marked; // marked[v] = is there an s-v path?
//记录顶点 从起点到一个顶点的已知路径上的最后一个顶点
private int[] edgeTo; // edgeTo[v] = last edge on s-v path,
private final int s; // source vertex
/**
* Computes a path between {@code s} and every other vertex in graph {@code G}.
* @param G the graph
* @param s the source vertex
* @throws IllegalArgumentException unless {@code 0 <= s < V}
*/
public DepthFirstPaths(Graph G, int s) {
this.s = s;
edgeTo = new int[G.V()];
marked = new boolean[G.V()];
validateVertex(s);
dfs(G, s);
}
// depth first search from v
private void dfs(Graph G, int v) {
marked[v] = true;
for (int w : G.adj(v)) {
if (!marked[w]) {
edgeTo[w] = v;
dfs(G, w);
}
}
}
/**
* Is there a path between the source vertex {@code s} and vertex {@code v}?
* @param v the vertex
* @return {@code true} if there is a path, {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return marked[v];
}
/**
* Returns a path between the source vertex {@code s} and vertex {@code v}, or
* {@code null} if no such path.
* @param v the vertex
* @return the sequence of vertices on a path between the source vertex
* {@code s} and vertex {@code v}, as an Iterable
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Integer> pathTo(int v) {
validateVertex(v);
if (!hasPathTo(v)) return null;
Stack<Integer> path = new Stack<Integer>();
for (int x = v; x != s; x = edgeTo[x])
path.push(x);
path.push(s);
return path;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
int V = marked.length;
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Unit tests the {@code DepthFirstPaths} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
Graph G = new Graph(in);
int s = Integer.parseInt(args[1]);
DepthFirstPaths dfs = new DepthFirstPaths(G, s);
for (int v = 0; v < G.V(); v++) {
if (dfs.hasPathTo(v)) {
StdOut.printf("%d to %d: ", s, v);
for (int x : dfs.pathTo(v)) {
if (x == s) StdOut.print(x);
else StdOut.print("-" + x);
}
StdOut.println();
}
else {
StdOut.printf("%d to %d: not connected\n", s, v);
}
}
}
}

要找出 s 到任意顶点 v 的路径,pathTo() 方法用变量 x 遍历整棵树,将 x 设为 edge[x] ,然后在到达 s 之前,将遇到的所有顶点都压入栈中。将这个栈返回为一个 Iterable 对象帮助用例遍历 s 到 v 的路径。

深度优先只是解决了从一个顶点到另一个顶点之间是否存在一条路径的问题

深度优先搜索是基于栈的

广度优先搜索 BFS

广度优先搜索是基于队列的

使用一个队列来保存所有已经被标记过但其邻接表还未被检查过的顶点;

先将起点加入队列,然后从队列中将起点删除,并将相邻顶点加入队列中,然后执行下面步骤:

  1. 取队列的下一个顶点 v 并标记它;
  2. 将与 v 相邻的所有未标记过的顶点加入队列;

重复上述步骤直到队列为空;

JAVA 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
public class BreadthFirstPaths {
private static final int INFINITY = Integer.MAX_VALUE;
private boolean[] marked; // marked[v] = is there an s-v path
private int[] edgeTo; // edgeTo[v] = previous edge on shortest s-v path
private int[] distTo; // distTo[v] = number of edges shortest s-v path
/**
* Computes the shortest path between the source vertex {@code s}
* and every other vertex in the graph {@code G}.
* @param G the graph
* @param s the source vertex
* @throws IllegalArgumentException unless {@code 0 <= s < V}
*/
public BreadthFirstPaths(Graph G, int s) {
marked = new boolean[G.V()];
distTo = new int[G.V()];
edgeTo = new int[G.V()];
validateVertex(s);
bfs(G, s);
assert check(G, s);
}
/**
* Computes the shortest path between any one of the source vertices in {@code sources}
* and every other vertex in graph {@code G}.
* @param G the graph
* @param sources the source vertices
* @throws IllegalArgumentException unless {@code 0 <= s < V} for each vertex
* {@code s} in {@code sources}
*/
public BreadthFirstPaths(Graph G, Iterable<Integer> sources) {
marked = new boolean[G.V()];
distTo = new int[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++)
distTo[v] = INFINITY;
validateVertices(sources);
bfs(G, sources);
}
// breadth-first search from a single source
private void bfs(Graph G, int s) {
Queue<Integer> q = new Queue<Integer>();
for (int v = 0; v < G.V(); v++)
distTo[v] = INFINITY;
distTo[s] = 0;
marked[s] = true;
q.enqueue(s);
while (!q.isEmpty()) {
int v = q.dequeue();
for (int w : G.adj(v)) {
if (!marked[w]) {
edgeTo[w] = v;
distTo[w] = distTo[v] + 1;
marked[w] = true;
q.enqueue(w);
}
}
}
}
// breadth-first search from multiple sources
private void bfs(Graph G, Iterable<Integer> sources) {
Queue<Integer> q = new Queue<Integer>();
for (int s : sources) {
marked[s] = true;
distTo[s] = 0;
q.enqueue(s);
}
while (!q.isEmpty()) {
int v = q.dequeue();
for (int w : G.adj(v)) {
if (!marked[w]) {
edgeTo[w] = v;
distTo[w] = distTo[v] + 1;
marked[w] = true;
q.enqueue(w);
}
}
}
}
/**
* Is there a path between the source vertex {@code s} (or sources) and vertex {@code v}?
* @param v the vertex
* @return {@code true} if there is a path, and {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return marked[v];
}
/**
* Returns the number of edges in a shortest path between the source vertex {@code s}
* (or sources) and vertex {@code v}?
* @param v the vertex
* @return the number of edges in a shortest path
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int distTo(int v) {
validateVertex(v);
return distTo[v];
}
/**
* Returns a shortest path between the source vertex {@code s} (or sources)
* and {@code v}, or {@code null} if no such path.
* @param v the vertex
* @return the sequence of vertices on a shortest path, as an Iterable
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Integer> pathTo(int v) {
validateVertex(v);
if (!hasPathTo(v)) return null;
Stack<Integer> path = new Stack<Integer>();
int x;
for (x = v; distTo[x] != 0; x = edgeTo[x])
path.push(x);
path.push(x);
return path;
}
// check optimality conditions for single source
private boolean check(Graph G, int s) {
// check that the distance of s = 0
if (distTo[s] != 0) {
StdOut.println("distance of source " + s + " to itself = " + distTo[s]);
return false;
}
// check that for each edge v-w dist[w] <= dist[v] + 1
// provided v is reachable from s
for (int v = 0; v < G.V(); v++) {
for (int w : G.adj(v)) {
if (hasPathTo(v) != hasPathTo(w)) {
StdOut.println("edge " + v + "-" + w);
StdOut.println("hasPathTo(" + v + ") = " + hasPathTo(v));
StdOut.println("hasPathTo(" + w + ") = " + hasPathTo(w));
return false;
}
if (hasPathTo(v) && (distTo[w] > distTo[v] + 1)) {
StdOut.println("edge " + v + "-" + w);
StdOut.println("distTo[" + v + "] = " + distTo[v]);
StdOut.println("distTo[" + w + "] = " + distTo[w]);
return false;
}
}
}
// check that v = edgeTo[w] satisfies distTo[w] = distTo[v] + 1
// provided v is reachable from s
for (int w = 0; w < G.V(); w++) {
if (!hasPathTo(w) || w == s) continue;
int v = edgeTo[w];
if (distTo[w] != distTo[v] + 1) {
StdOut.println("shortest path edge " + v + "-" + w);
StdOut.println("distTo[" + v + "] = " + distTo[v]);
StdOut.println("distTo[" + w + "] = " + distTo[w]);
return false;
}
}
return true;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
int V = marked.length;
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertices(Iterable<Integer> vertices) {
if (vertices == null) {
throw new IllegalArgumentException("argument is null");
}
int V = marked.length;
for (int v : vertices) {
if (v < 0 || v >= V) {
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
}
}
/**
* Unit tests the {@code BreadthFirstPaths} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
Graph G = new Graph(in);
// StdOut.println(G);
int s = Integer.parseInt(args[1]);
BreadthFirstPaths bfs = new BreadthFirstPaths(G, s);
for (int v = 0; v < G.V(); v++) {
if (bfs.hasPathTo(v)) {
StdOut.printf("%d to %d (%d): ", s, v, bfs.distTo(v));
for (int x : bfs.pathTo(v)) {
if (x == s) StdOut.print(x);
else StdOut.print("-" + x);
}
StdOut.println();
}
else {
StdOut.printf("%d to %d (-): not connected\n", s, v);
}
}
}
}

连通分量

基于深度优先搜索的解决方案如下,同样可以使用 union_find 来检查连通性;

java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
public class CC {
private boolean[] marked; // marked[v] = has vertex v been marked?
//顶点 v 的连通分量
private int[] id; // id[v] = id of connected component containing v
// 连通分量包含多少个顶点
private int[] size; // size[id] = number of vertices in given component
//连通分量总数
private int count; // number of connected components
/**
* Computes the connected components of the undirected graph {@code G}.
*
* @param G the undirected graph
*/
public CC(Graph G) {
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
for (int v = 0; v < G.V(); v++) {
if (!marked[v]) {
dfs(G, v);
count++;
}
}
}
/**
* Computes the connected components of the edge-weighted graph {@code G}.
*
* @param G the edge-weighted graph
*/
public CC(EdgeWeightedGraph G) {
marked = new boolean[G.V()];
id = new int[G.V()];
size = new int[G.V()];
for (int v = 0; v < G.V(); v++) {
if (!marked[v]) {
dfs(G, v);
count++;
}
}
}
// depth-first search for a Graph
private void dfs(Graph G, int v) {
marked[v] = true;
id[v] = count;
size[count]++;
for (int w : G.adj(v)) {
if (!marked[w]) {
dfs(G, w);
}
}
}
// depth-first search for an EdgeWeightedGraph
private void dfs(EdgeWeightedGraph G, int v) {
marked[v] = true;
id[v] = count;
size[count]++;
for (Edge e : G.adj(v)) {
int w = e.other(v);
if (!marked[w]) {
dfs(G, w);
}
}
}
/**
* Returns the component id of the connected component containing vertex {@code v}.
*
* @param v the vertex
* @return the component id of the connected component containing vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int id(int v) {
validateVertex(v);
return id[v];
}
/**
* Returns the number of vertices in the connected component containing vertex {@code v}.
*
* @param v the vertex
* @return the number of vertices in the connected component containing vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int size(int v) {
validateVertex(v);
return size[id[v]];
}
/**
* Returns the number of connected components in the graph {@code G}.
*
* @return the number of connected components in the graph {@code G}
*/
public int count() {
return count;
}
/**
* Returns true if vertices {@code v} and {@code w} are in the same
* connected component.
*
* @param v one vertex
* @param w the other vertex
* @return {@code true} if vertices {@code v} and {@code w} are in the same
* connected component; {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
* @throws IllegalArgumentException unless {@code 0 <= w < V}
*/
public boolean connected(int v, int w) {
validateVertex(v);
validateVertex(w);
return id(v) == id(w);
}
/**
* Returns true if vertices {@code v} and {@code w} are in the same
* connected component.
*
* @param v one vertex
* @param w the other vertex
* @return {@code true} if vertices {@code v} and {@code w} are in the same
* connected component; {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
* @throws IllegalArgumentException unless {@code 0 <= w < V}
* @deprecated Replaced by {@link #connected(int, int)}.
*/
@Deprecated
public boolean areConnected(int v, int w) {
validateVertex(v);
validateVertex(w);
return id(v) == id(w);
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
int V = marked.length;
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Unit tests the {@code CC} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
Graph G = new Graph(in);
CC cc = new CC(G);
// number of connected components
int m = cc.count();
StdOut.println(m + " components");
// compute list of vertices in each connected component
Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m];
for (int i = 0; i < m; i++) {
components[i] = new Queue<Integer>();
}
for (int v = 0; v < G.V(); v++) {
components[cc.id(v)].enqueue(v);
}
// print results
for (int i = 0; i < m; i++) {
for (int v : components[i]) {
StdOut.print(v + " ");
}
StdOut.println();
}
}
}

检测环

给定的图是不是无环图?

java 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class Cycle {
private boolean[] marked;
private int[] edgeTo;
private Stack<Integer> cycle;
/**
* Determines whether the undirected graph {@code G} has a cycle and,
* if so, finds such a cycle.
*
* @param G the undirected graph
*/
public Cycle(Graph G) {
if (hasSelfLoop(G)) return;
if (hasParallelEdges(G)) return;
marked = new boolean[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++)
if (!marked[v])
dfs(G, -1, v);
}
// does this graph have a self loop?
// side effect: initialize cycle to be self loop
private boolean hasSelfLoop(Graph G) {
for (int v = 0; v < G.V(); v++) {
for (int w : G.adj(v)) {
if (v == w) {
cycle = new Stack<Integer>();
cycle.push(v);
cycle.push(v);
return true;
}
}
}
return false;
}
// does this graph have two parallel edges?
// side effect: initialize cycle to be two parallel edges
private boolean hasParallelEdges(Graph G) {
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++) {
// check for parallel edges incident to v
for (int w : G.adj(v)) {
if (marked[w]) {
cycle = new Stack<Integer>();
cycle.push(v);
cycle.push(w);
cycle.push(v);
return true;
}
marked[w] = true;
}
// reset so marked[v] = false for all v
for (int w : G.adj(v)) {
marked[w] = false;
}
}
return false;
}
/**
* Returns true if the graph {@code G} has a cycle.
*
* @return {@code true} if the graph has a cycle; {@code false} otherwise
*/
public boolean hasCycle() {
return cycle != null;
}
/**
* Returns a cycle in the graph {@code G}.
* @return a cycle if the graph {@code G} has a cycle,
* and {@code null} otherwise
*/
public Iterable<Integer> cycle() {
return cycle;
}
private void dfs(Graph G, int u, int v) {
marked[v] = true;
for (int w : G.adj(v)) {
// short circuit if cycle already found
if (cycle != null) return;
if (!marked[w]) {
edgeTo[w] = v;
dfs(G, v, w);
}
// check for cycle (but disregard reverse of edge leading to v)
else if (w != u) {
cycle = new Stack<Integer>();
for (int x = v; x != w; x = edgeTo[x]) {
cycle.push(x);
}
cycle.push(w);
cycle.push(v);
}
}
}
/**
* Unit tests the {@code Cycle} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
Graph G = new Graph(in);
Cycle finder = new Cycle(G);
if (finder.hasCycle()) {
for (int v : finder.cycle()) {
StdOut.print(v + " ");
}
StdOut.println();
}
else {
StdOut.println("Graph is acyclic");
}
}
}

有向图

最小生成树

加权图

定义:一种为每条边关联一个权值或者是成本的模型;

最小生成树

定义:图的生成树是它的一棵含有其所有顶点的无环连通子图。一幅加权图的最小生成树(MST) 是它的一棵权值(树中所有边的权值之和)最小的生成树;

切分定理

切分定义:图的一种切分是将图的所有顶点分为两个非空且不重叠的两个集合。横切边是一条连接两个属于不同集合的顶点的边;

切分定理:在一幅加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图的最小生成树;

贪心算法

定义:下面这种方法会将含有 V 个顶点的任意加权连通图中属于最小生成树的边标记为黑色:初始状态下所有边均为灰色, 找到一种切分,它产生的横切边均不为黑色。将它权重最小的横切边标记为黑色。反复,直到标记了V - 1条黑色边为止;

加权无向图

在邻接表中,在链表的结点中增加一个权重域;

Edge 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class Edge implements Comparable<Edge> {
private final int v;
private final int w;
private final double weight;
/**
* Initializes an edge between vertices {@code v} and {@code w} of
* the given {@code weight}.
*
* @param v one vertex
* @param w the other vertex
* @param weight the weight of this edge
* @throws IllegalArgumentException if either {@code v} or {@code w}
* is a negative integer
* @throws IllegalArgumentException if {@code weight} is {@code NaN}
*/
public Edge(int v, int w, double weight) {
if (v < 0) throw new IllegalArgumentException("vertex index must be a nonnegative integer");
if (w < 0) throw new IllegalArgumentException("vertex index must be a nonnegative integer");
if (Double.isNaN(weight)) throw new IllegalArgumentException("Weight is NaN");
this.v = v;
this.w = w;
this.weight = weight;
}
/**
* Returns the weight of this edge.
*
* @return the weight of this edge
*/
public double weight() {
return weight;
}
/**
* Returns either endpoint of this edge.
*
* @return either endpoint of this edge
*/
public int either() {
return v;
}
/**
* Returns the endpoint of this edge that is different from the given vertex.
*
* @param vertex one endpoint of this edge
* @return the other endpoint of this edge
* @throws IllegalArgumentException if the vertex is not one of the
* endpoints of this edge
*/
public int other(int vertex) {
if (vertex == v) return w;
else if (vertex == w) return v;
else throw new IllegalArgumentException("Illegal endpoint");
}
/**
* Compares two edges by weight.
* Note that {@code compareTo()} is not consistent with {@code equals()},
* which uses the reference equality implementation inherited from {@code Object}.
*
* @param that the other edge
* @return a negative integer, zero, or positive integer depending on whether
* the weight of this is less than, equal to, or greater than the
* argument edge
*/
@Override
public int compareTo(Edge that) {
return Double.compare(this.weight, that.weight);
}
/**
* Returns a string representation of this edge.
*
* @return a string representation of this edge
*/
public String toString() {
return String.format("%d-%d %.5f", v, w, weight);
}
/**
* Unit tests the {@code Edge} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
Edge e = new Edge(12, 34, 5.67);
StdOut.println(e);
}
}

EdgeWeightedGraph 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
public class EdgeWeightedGraph {
private static final String NEWLINE = System.getProperty("line.separator");
private final int V;
private int E;
private Bag<Edge>[] adj;
/**
* Initializes an empty edge-weighted graph with {@code V} vertices and 0 edges.
*
* @param V the number of vertices
* @throws IllegalArgumentException if {@code V < 0}
*/
public EdgeWeightedGraph(int V) {
if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
this.V = V;
this.E = 0;
adj = (Bag<Edge>[]) new Bag[V];
for (int v = 0; v < V; v++) {
adj[v] = new Bag<Edge>();
}
}
/**
* Initializes a random edge-weighted graph with {@code V} vertices and <em>E</em> edges.
*
* @param V the number of vertices
* @param E the number of edges
* @throws IllegalArgumentException if {@code V < 0}
* @throws IllegalArgumentException if {@code E < 0}
*/
public EdgeWeightedGraph(int V, int E) {
this(V);
if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
for (int i = 0; i < E; i++) {
int v = StdRandom.uniform(V);
int w = StdRandom.uniform(V);
double weight = Math.round(100 * StdRandom.uniform()) / 100.0;
Edge e = new Edge(v, w, weight);
addEdge(e);
}
}
/**
* Initializes an edge-weighted graph from an input stream.
* The format is the number of vertices <em>V</em>,
* followed by the number of edges <em>E</em>,
* followed by <em>E</em> pairs of vertices and edge weights,
* with each entry separated by whitespace.
*
* @param in the input stream
* @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range
* @throws IllegalArgumentException if the number of vertices or edges is negative
*/
public EdgeWeightedGraph(In in) {
this(in.readInt());
int E = in.readInt();
if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
for (int i = 0; i < E; i++) {
int v = in.readInt();
int w = in.readInt();
validateVertex(v);
validateVertex(w);
double weight = in.readDouble();
Edge e = new Edge(v, w, weight);
addEdge(e);
}
}
/**
* Initializes a new edge-weighted graph that is a deep copy of {@code G}.
*
* @param G the edge-weighted graph to copy
*/
public EdgeWeightedGraph(EdgeWeightedGraph G) {
this(G.V());
this.E = G.E();
for (int v = 0; v < G.V(); v++) {
// reverse so that adjacency list is in same order as original
Stack<Edge> reverse = new Stack<Edge>();
for (Edge e : G.adj[v]) {
reverse.push(e);
}
for (Edge e : reverse) {
adj[v].add(e);
}
}
}
/**
* Returns the number of vertices in this edge-weighted graph.
*
* @return the number of vertices in this edge-weighted graph
*/
public int V() {
return V;
}
/**
* Returns the number of edges in this edge-weighted graph.
*
* @return the number of edges in this edge-weighted graph
*/
public int E() {
return E;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Adds the undirected edge {@code e} to this edge-weighted graph.
*
* @param e the edge
* @throws IllegalArgumentException unless both endpoints are between {@code 0} and {@code V-1}
*/
public void addEdge(Edge e) {
int v = e.either();
int w = e.other(v);
validateVertex(v);
validateVertex(w);
adj[v].add(e);
adj[w].add(e);
E++;
}
/**
* Returns the edges incident on vertex {@code v}.
*
* @param v the vertex
* @return the edges incident on vertex {@code v} as an Iterable
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Edge> adj(int v) {
validateVertex(v);
return adj[v];
}
/**
* Returns the degree of vertex {@code v}.
*
* @param v the vertex
* @return the degree of vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int degree(int v) {
validateVertex(v);
return adj[v].size();
}
/**
* Returns all edges in this edge-weighted graph.
* To iterate over the edges in this edge-weighted graph, use foreach notation:
* {@code for (Edge e : G.edges())}.
*
* @return all edges in this edge-weighted graph, as an iterable
*/
public Iterable<Edge> edges() {
Bag<Edge> list = new Bag<Edge>();
for (int v = 0; v < V; v++) {
int selfLoops = 0;
for (Edge e : adj(v)) {
if (e.other(v) > v) {
list.add(e);
}
// add only one copy of each self loop (self loops will be consecutive)
else if (e.other(v) == v) {
if (selfLoops % 2 == 0) list.add(e);
selfLoops++;
}
}
}
return list;
}
/**
* Returns a string representation of the edge-weighted graph.
* This method takes time proportional to <em>E</em> + <em>V</em>.
*
* @return the number of vertices <em>V</em>, followed by the number of edges <em>E</em>,
* followed by the <em>V</em> adjacency lists of edges
*/
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " " + E + NEWLINE);
for (int v = 0; v < V; v++) {
s.append(v + ": ");
for (Edge e : adj[v]) {
s.append(e + " ");
}
s.append(NEWLINE);
}
return s.toString();
}
/**
* Unit tests the {@code EdgeWeightedGraph} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
StdOut.println(G);
}
}

Prim 算法

命题:Prim 算法能够得到任意加权连通图的最小生成树;

从一棵树的顶点开始,每次总是将下一条连接树的顶点与它的横切边加入树中;

Lazy Prim 延时实现
  • 一条优先队列保存所有的横切边, 将失效的边保留在队列中;
  • 一个由顶点索引的数组来标记树的顶点;
  • 一条队列来保存最小生成树的边;

Java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public class LazyPrimMST {
private static final double FLOATING_POINT_EPSILON = 1E-12;
private double weight; // total weight of MST
//最小生成树的边
private Queue<Edge> mst; // edges in the MST
private boolean[] marked; // marked[v] = true if v on tree
//包含所有的横切边
private MinPQ<Edge> pq; // edges with one endpoint in tree
/**
* Compute a minimum spanning tree (or forest) of an edge-weighted graph.
* @param G the edge-weighted graph
*/
public LazyPrimMST(EdgeWeightedGraph G) {
mst = new Queue<Edge>();
pq = new MinPQ<Edge>();
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++) // run Prim from all vertices to
if (!marked[v]) prim(G, v); // get a minimum spanning forest
// check optimality conditions
assert check(G);
}
// run Prim's algorithm
private void prim(EdgeWeightedGraph G, int s) {
scan(G, s);
while (!pq.isEmpty()) { // better to stop when mst has V-1 edges
Edge e = pq.delMin(); // smallest edge on pq
int v = e.either(), w = e.other(v); // two endpoints
assert marked[v] || marked[w];
if (marked[v] && marked[w]) continue; // lazy, both v and w already scanned
mst.enqueue(e); // add e to MST
weight += e.weight();
if (!marked[v]) scan(G, v); // v becomes part of tree
if (!marked[w]) scan(G, w); // w becomes part of tree
}
}
// add all edges e incident to v onto pq if the other endpoint has not yet been scanned
private void scan(EdgeWeightedGraph G, int v) {
assert !marked[v];
marked[v] = true;
for (Edge e : G.adj(v))
if (!marked[e.other(v)]) pq.insert(e);
}
/**
* Returns the edges in a minimum spanning tree (or forest).
* @return the edges in a minimum spanning tree (or forest) as
* an iterable of edges
*/
public Iterable<Edge> edges() {
return mst;
}
/**
* Returns the sum of the edge weights in a minimum spanning tree (or forest).
* @return the sum of the edge weights in a minimum spanning tree (or forest)
*/
public double weight() {
return weight;
}
// check optimality conditions (takes time proportional to E V lg* V)
private boolean check(EdgeWeightedGraph G) {
// check weight
double totalWeight = 0.0;
for (Edge e : edges()) {
totalWeight += e.weight();
}
if (Math.abs(totalWeight - weight()) > FLOATING_POINT_EPSILON) {
System.err.printf("Weight of edges does not equal weight(): %f vs. %f\n", totalWeight, weight());
return false;
}
// check that it is acyclic
UF uf = new UF(G.V());
for (Edge e : edges()) {
int v = e.either(), w = e.other(v);
if (uf.connected(v, w)) {
System.err.println("Not a forest");
return false;
}
uf.union(v, w);
}
// check that it is a spanning forest
for (Edge e : G.edges()) {
int v = e.either(), w = e.other(v);
if (!uf.connected(v, w)) {
System.err.println("Not a spanning forest");
return false;
}
}
// check that it is a minimal spanning forest (cut optimality conditions)
for (Edge e : edges()) {
// all edges in MST except e
uf = new UF(G.V());
for (Edge f : mst) {
int x = f.either(), y = f.other(x);
if (f != e) uf.union(x, y);
}
// check that e is min weight edge in crossing cut
for (Edge f : G.edges()) {
int x = f.either(), y = f.other(x);
if (!uf.connected(x, y)) {
if (f.weight() < e.weight()) {
System.err.println("Edge " + f + " violates cut optimality conditions");
return false;
}
}
}
}
return true;
}
/**
* Unit tests the {@code LazyPrimMST} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
LazyPrimMST mst = new LazyPrimMST(G);
for (Edge e : mst.edges()) {
StdOut.println(e);
}
StdOut.printf("%.5f\n", mst.weight());
}
}

相应的 MinPQ 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
public class MinPQ<Key> implements Iterable<Key> {
private Key[] pq; // store items at indices 1 to n
private int n; // number of items on priority queue
private Comparator<Key> comparator; // optional comparator
/**
* Initializes an empty priority queue with the given initial capacity.
*
* @param initCapacity the initial capacity of this priority queue
*/
public MinPQ(int initCapacity) {
pq = (Key[]) new Object[initCapacity + 1];
n = 0;
}
/**
* Initializes an empty priority queue.
*/
public MinPQ() {
this(1);
}
/**
* Initializes an empty priority queue with the given initial capacity,
* using the given comparator.
*
* @param initCapacity the initial capacity of this priority queue
* @param comparator the order in which to compare the keys
*/
public MinPQ(int initCapacity, Comparator<Key> comparator) {
this.comparator = comparator;
pq = (Key[]) new Object[initCapacity + 1];
n = 0;
}
/**
* Initializes an empty priority queue using the given comparator.
*
* @param comparator the order in which to compare the keys
*/
public MinPQ(Comparator<Key> comparator) {
this(1, comparator);
}
/**
* Initializes a priority queue from the array of keys.
* <p>
* Takes time proportional to the number of keys, using sink-based heap construction.
*
* @param keys the array of keys
*/
public MinPQ(Key[] keys) {
n = keys.length;
pq = (Key[]) new Object[keys.length + 1];
for (int i = 0; i < n; i++)
pq[i+1] = keys[i];
for (int k = n/2; k >= 1; k--)
sink(k);
assert isMinHeap();
}
/**
* Returns true if this priority queue is empty.
*
* @return {@code true} if this priority queue is empty;
* {@code false} otherwise
*/
public boolean isEmpty() {
return n == 0;
}
/**
* Returns the number of keys on this priority queue.
*
* @return the number of keys on this priority queue
*/
public int size() {
return n;
}
/**
* Returns a smallest key on this priority queue.
*
* @return a smallest key on this priority queue
* @throws NoSuchElementException if this priority queue is empty
*/
public Key min() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
return pq[1];
}
// helper function to double the size of the heap array
private void resize(int capacity) {
assert capacity > n;
Key[] temp = (Key[]) new Object[capacity];
for (int i = 1; i <= n; i++) {
temp[i] = pq[i];
}
pq = temp;
}
/**
* Adds a new key to this priority queue.
*
* @param x the key to add to this priority queue
*/
public void insert(Key x) {
// double size of array if necessary
if (n == pq.length - 1) resize(2 * pq.length);
// add x, and percolate it up to maintain heap invariant
pq[++n] = x;
swim(n);
assert isMinHeap();
}
/**
* Removes and returns a smallest key on this priority queue.
*
* @return a smallest key on this priority queue
* @throws NoSuchElementException if this priority queue is empty
*/
public Key delMin() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
Key min = pq[1];
exch(1, n--);
sink(1);
pq[n+1] = null; // to avoid loiterig and help with garbage collection
if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2);
assert isMinHeap();
return min;
}
/***************************************************************************
* Helper functions to restore the heap invariant.
***************************************************************************/
private void swim(int k) {
while (k > 1 && greater(k/2, k)) {
exch(k, k/2);
k = k/2;
}
}
private void sink(int k) {
while (2*k <= n) {
int j = 2*k;
if (j < n && greater(j, j+1)) j++;
if (!greater(k, j)) break;
exch(k, j);
k = j;
}
}
/***************************************************************************
* Helper functions for compares and swaps.
***************************************************************************/
private boolean greater(int i, int j) {
if (comparator == null) {
return ((Comparable<Key>) pq[i]).compareTo(pq[j]) > 0;
}
else {
return comparator.compare(pq[i], pq[j]) > 0;
}
}
private void exch(int i, int j) {
Key swap = pq[i];
pq[i] = pq[j];
pq[j] = swap;
}
// is pq[1..N] a min heap?
private boolean isMinHeap() {
return isMinHeap(1);
}
// is subtree of pq[1..n] rooted at k a min heap?
private boolean isMinHeap(int k) {
if (k > n) return true;
int left = 2*k;
int right = 2*k + 1;
if (left <= n && greater(k, left)) return false;
if (right <= n && greater(k, right)) return false;
return isMinHeap(left) && isMinHeap(right);
}
/**
* Returns an iterator that iterates over the keys on this priority queue
* in ascending order.
* <p>
* The iterator doesn't implement {@code remove()} since it's optional.
*
* @return an iterator that iterates over the keys in ascending order
*/
public Iterator<Key> iterator() {
return new HeapIterator();
}
private class HeapIterator implements Iterator<Key> {
// create a new pq
private MinPQ<Key> copy;
// add all items to copy of heap
// takes linear time since already in heap order so no keys move
public HeapIterator() {
if (comparator == null) copy = new MinPQ<Key>(size());
else copy = new MinPQ<Key>(size(), comparator);
for (int i = 1; i <= n; i++)
copy.insert(pq[i]);
}
public boolean hasNext() { return !copy.isEmpty(); }
public void remove() { throw new UnsupportedOperationException(); }
public Key next() {
if (!hasNext()) throw new NoSuchElementException();
return copy.delMin();
}
}
/**
* Unit tests the {@code MinPQ} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
MinPQ<String> pq = new MinPQ<String>();
while (!StdIn.isEmpty()) {
String item = StdIn.readString();
if (!item.equals("-")) pq.insert(item);
else if (!pq.isEmpty()) StdOut.print(pq.delMin() + " ");
}
StdOut.println("(" + pq.size() + " left on pq)");
}
}
Prim 即时实现

关注的是每个顶点横切边中的最小值;

将失效的边从队列中删除掉;

将有效的横切边都保存在一条索引优先队列中。

Java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
public class PrimMST {
private static final double FLOATING_POINT_EPSILON = 1E-12;
private Edge[] edgeTo; // edgeTo[v] = shortest edge from tree vertex to non-tree vertex
private double[] distTo; // distTo[v] = weight of shortest such edge
private boolean[] marked; // marked[v] = true if v on tree, false otherwise
private IndexMinPQ<Double> pq;
/**
* Compute a minimum spanning tree (or forest) of an edge-weighted graph.
* @param G the edge-weighted graph
*/
public PrimMST(EdgeWeightedGraph G) {
edgeTo = new Edge[G.V()];
distTo = new double[G.V()];
marked = new boolean[G.V()];
pq = new IndexMinPQ<Double>(G.V());
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
for (int v = 0; v < G.V(); v++) // run from each vertex to find
if (!marked[v]) prim(G, v); // minimum spanning forest
// check optimality conditions
assert check(G);
}
// run Prim's algorithm in graph G, starting from vertex s
private void prim(EdgeWeightedGraph G, int s) {
distTo[s] = 0.0;
pq.insert(s, distTo[s]);
while (!pq.isEmpty()) {
int v = pq.delMin();
scan(G, v);
}
}
// scan vertex v
private void scan(EdgeWeightedGraph G, int v) {
marked[v] = true;
for (Edge e : G.adj(v)) {
int w = e.other(v);
if (marked[w]) continue; // v-w is obsolete edge
if (e.weight() < distTo[w]) {
distTo[w] = e.weight();
edgeTo[w] = e;
if (pq.contains(w)) pq.decreaseKey(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
}
/**
* Returns the edges in a minimum spanning tree (or forest).
* @return the edges in a minimum spanning tree (or forest) as
* an iterable of edges
*/
public Iterable<Edge> edges() {
Queue<Edge> mst = new Queue<Edge>();
for (int v = 0; v < edgeTo.length; v++) {
Edge e = edgeTo[v];
if (e != null) {
mst.enqueue(e);
}
}
return mst;
}
/**
* Returns the sum of the edge weights in a minimum spanning tree (or forest).
* @return the sum of the edge weights in a minimum spanning tree (or forest)
*/
public double weight() {
double weight = 0.0;
for (Edge e : edges())
weight += e.weight();
return weight;
}
// check optimality conditions (takes time proportional to E V lg* V)
private boolean check(EdgeWeightedGraph G) {
// check weight
double totalWeight = 0.0;
for (Edge e : edges()) {
totalWeight += e.weight();
}
if (Math.abs(totalWeight - weight()) > FLOATING_POINT_EPSILON) {
System.err.printf("Weight of edges does not equal weight(): %f vs. %f\n", totalWeight, weight());
return false;
}
// check that it is acyclic
UF uf = new UF(G.V());
for (Edge e : edges()) {
int v = e.either(), w = e.other(v);
if (uf.connected(v, w)) {
System.err.println("Not a forest");
return false;
}
uf.union(v, w);
}
// check that it is a spanning forest
for (Edge e : G.edges()) {
int v = e.either(), w = e.other(v);
if (!uf.connected(v, w)) {
System.err.println("Not a spanning forest");
return false;
}
}
// check that it is a minimal spanning forest (cut optimality conditions)
for (Edge e : edges()) {
// all edges in MST except e
uf = new UF(G.V());
for (Edge f : edges()) {
int x = f.either(), y = f.other(x);
if (f != e) uf.union(x, y);
}
// check that e is min weight edge in crossing cut
for (Edge f : G.edges()) {
int x = f.either(), y = f.other(x);
if (!uf.connected(x, y)) {
if (f.weight() < e.weight()) {
System.err.println("Edge " + f + " violates cut optimality conditions");
return false;
}
}
}
}
return true;
}
/**
* Unit tests the {@code PrimMST} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
PrimMST mst = new PrimMST(G);
for (Edge e : mst.edges()) {
StdOut.println(e);
}
StdOut.printf("%.5f\n", mst.weight());
}
}

相应的 IndexMinPQ 算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
public class IndexMinPQ<Key extends Comparable<Key>> implements Iterable<Integer> {
private int maxN; // maximum number of elements on PQ
private int n; // number of elements on PQ
private int[] pq; // binary heap using 1-based indexing
private int[] qp; // inverse of pq - qp[pq[i]] = pq[qp[i]] = i
private Key[] keys; // keys[i] = priority of i
/**
* Initializes an empty indexed priority queue with indices between {@code 0}
* and {@code maxN - 1}.
* @param maxN the keys on this priority queue are index from {@code 0}
* {@code maxN - 1}
* @throws IllegalArgumentException if {@code maxN < 0}
*/
public IndexMinPQ(int maxN) {
if (maxN < 0) throw new IllegalArgumentException();
this.maxN = maxN;
n = 0;
keys = (Key[]) new Comparable[maxN + 1]; // make this of length maxN??
pq = new int[maxN + 1];
qp = new int[maxN + 1]; // make this of length maxN??
for (int i = 0; i <= maxN; i++)
qp[i] = -1;
}
/**
* Returns true if this priority queue is empty.
*
* @return {@code true} if this priority queue is empty;
* {@code false} otherwise
*/
public boolean isEmpty() {
return n == 0;
}
/**
* Is {@code i} an index on this priority queue?
*
* @param i an index
* @return {@code true} if {@code i} is an index on this priority queue;
* {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
*/
public boolean contains(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
return qp[i] != -1;
}
/**
* Returns the number of keys on this priority queue.
*
* @return the number of keys on this priority queue
*/
public int size() {
return n;
}
/**
* Associates key with index {@code i}.
*
* @param i an index
* @param key the key to associate with index {@code i}
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws IllegalArgumentException if there already is an item associated
* with index {@code i}
*/
public void insert(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (contains(i)) throw new IllegalArgumentException("index is already in the priority queue");
n++;
qp[i] = n;
pq[n] = i;
keys[i] = key;
swim(n);
}
/**
* Returns an index associated with a minimum key.
*
* @return an index associated with a minimum key
* @throws NoSuchElementException if this priority queue is empty
*/
public int minIndex() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
return pq[1];
}
/**
* Returns a minimum key.
*
* @return a minimum key
* @throws NoSuchElementException if this priority queue is empty
*/
public Key minKey() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
return keys[pq[1]];
}
/**
* Removes a minimum key and returns its associated index.
* @return an index associated with a minimum key
* @throws NoSuchElementException if this priority queue is empty
*/
public int delMin() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
int min = pq[1];
exch(1, n--);
sink(1);
assert min == pq[n+1];
qp[min] = -1; // delete
keys[min] = null; // to help with garbage collection
pq[n+1] = -1; // not needed
return min;
}
/**
* Returns the key associated with index {@code i}.
*
* @param i the index of the key to return
* @return the key associated with index {@code i}
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public Key keyOf(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
else return keys[i];
}
/**
* Change the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to change
* @param key change the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void changeKey(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
keys[i] = key;
swim(qp[i]);
sink(qp[i]);
}
/**
* Change the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to change
* @param key change the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @deprecated Replaced by {@code changeKey(int, Key)}.
*/
@Deprecated
public void change(int i, Key key) {
changeKey(i, key);
}
/**
* Decrease the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to decrease
* @param key decrease the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws IllegalArgumentException if {@code key >= keyOf(i)}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void decreaseKey(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
if (keys[i].compareTo(key) <= 0)
throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key");
keys[i] = key;
swim(qp[i]);
}
/**
* Increase the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to increase
* @param key increase the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws IllegalArgumentException if {@code key <= keyOf(i)}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void increaseKey(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
if (keys[i].compareTo(key) >= 0)
throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key");
keys[i] = key;
sink(qp[i]);
}
/**
* Remove the key associated with index {@code i}.
*
* @param i the index of the key to remove
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void delete(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
int index = qp[i];
exch(index, n--);
swim(index);
sink(index);
keys[i] = null;
qp[i] = -1;
}
/***************************************************************************
* General helper functions.
***************************************************************************/
private boolean greater(int i, int j) {
return keys[pq[i]].compareTo(keys[pq[j]]) > 0;
}
private void exch(int i, int j) {
int swap = pq[i];
pq[i] = pq[j];
pq[j] = swap;
qp[pq[i]] = i;
qp[pq[j]] = j;
}
/***************************************************************************
* Heap helper functions.
***************************************************************************/
private void swim(int k) {
while (k > 1 && greater(k/2, k)) {
exch(k, k/2);
k = k/2;
}
}
private void sink(int k) {
while (2*k <= n) {
int j = 2*k;
if (j < n && greater(j, j+1)) j++;
if (!greater(k, j)) break;
exch(k, j);
k = j;
}
}
/***************************************************************************
* Iterators.
***************************************************************************/
/**
* Returns an iterator that iterates over the keys on the
* priority queue in ascending order.
* The iterator doesn't implement {@code remove()} since it's optional.
*
* @return an iterator that iterates over the keys in ascending order
*/
public Iterator<Integer> iterator() { return new HeapIterator(); }
private class HeapIterator implements Iterator<Integer> {
// create a new pq
private IndexMinPQ<Key> copy;
// add all elements to copy of heap
// takes linear time since already in heap order so no keys move
public HeapIterator() {
copy = new IndexMinPQ<Key>(pq.length - 1);
for (int i = 1; i <= n; i++)
copy.insert(pq[i], keys[pq[i]]);
}
public boolean hasNext() { return !copy.isEmpty(); }
public void remove() { throw new UnsupportedOperationException(); }
public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
return copy.delMin();
}
}
/**
* Unit tests the {@code IndexMinPQ} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
// insert a bunch of strings
String[] strings = { "it", "was", "the", "best", "of", "times", "it", "was", "the", "worst" };
IndexMinPQ<String> pq = new IndexMinPQ<String>(strings.length);
for (int i = 0; i < strings.length; i++) {
pq.insert(i, strings[i]);
}
// delete and print each key
while (!pq.isEmpty()) {
int i = pq.delMin();
StdOut.println(i + " " + strings[i]);
}
StdOut.println();
// reinsert the same strings
for (int i = 0; i < strings.length; i++) {
pq.insert(i, strings[i]);
}
// print each key using the iterator
for (int i : pq) {
StdOut.println(i + " " + strings[i]);
}
while (!pq.isEmpty()) {
pq.delMin();
}
}
}

Kruskal 算法

思想:按照边的权重顺序(从小到大)来处理它们;

java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class KruskalMST {
private static final double FLOATING_POINT_EPSILON = 1E-12;
private double weight; // weight of MST
private Queue<Edge> mst = new Queue<Edge>(); // edges in MST
/**
* Compute a minimum spanning tree (or forest) of an edge-weighted graph.
* @param G the edge-weighted graph
*/
public KruskalMST(EdgeWeightedGraph G) {
// more efficient to build heap by passing array of edges
MinPQ<Edge> pq = new MinPQ<Edge>();
for (Edge e : G.edges()) {
pq.insert(e);
}
// run greedy algorithm
UF uf = new UF(G.V());
while (!pq.isEmpty() && mst.size() < G.V() - 1) {
Edge e = pq.delMin();
int v = e.either();
int w = e.other(v);
if (!uf.connected(v, w)) { // v-w does not create a cycle
uf.union(v, w); // merge v and w components
mst.enqueue(e); // add edge e to mst
weight += e.weight();
}
}
// check optimality conditions
assert check(G);
}
/**
* Returns the edges in a minimum spanning tree (or forest).
* @return the edges in a minimum spanning tree (or forest) as
* an iterable of edges
*/
public Iterable<Edge> edges() {
return mst;
}
/**
* Returns the sum of the edge weights in a minimum spanning tree (or forest).
* @return the sum of the edge weights in a minimum spanning tree (or forest)
*/
public double weight() {
return weight;
}
// check optimality conditions (takes time proportional to E V lg* V)
private boolean check(EdgeWeightedGraph G) {
// check total weight
double total = 0.0;
for (Edge e : edges()) {
total += e.weight();
}
if (Math.abs(total - weight()) > FLOATING_POINT_EPSILON) {
System.err.printf("Weight of edges does not equal weight(): %f vs. %f\n", total, weight());
return false;
}
// check that it is acyclic
UF uf = new UF(G.V());
for (Edge e : edges()) {
int v = e.either(), w = e.other(v);
if (uf.connected(v, w)) {
System.err.println("Not a forest");
return false;
}
uf.union(v, w);
}
// check that it is a spanning forest
for (Edge e : G.edges()) {
int v = e.either(), w = e.other(v);
if (!uf.connected(v, w)) {
System.err.println("Not a spanning forest");
return false;
}
}
// check that it is a minimal spanning forest (cut optimality conditions)
for (Edge e : edges()) {
// all edges in MST except e
uf = new UF(G.V());
for (Edge f : mst) {
int x = f.either(), y = f.other(x);
if (f != e) uf.union(x, y);
}
// check that e is min weight edge in crossing cut
for (Edge f : G.edges()) {
int x = f.either(), y = f.other(x);
if (!uf.connected(x, y)) {
if (f.weight() < e.weight()) {
System.err.println("Edge " + f + " violates cut optimality conditions");
return false;
}
}
}
}
return true;
}
/**
* Unit tests the {@code KruskalMST} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
KruskalMST mst = new KruskalMST(G);
for (Edge e : mst.edges()) {
StdOut.println(e);
}
StdOut.printf("%.5f\n", mst.weight());
}
}

最短路径

定义:在一幅加权有向图中,从顶点s 到顶点 t 的最短路径是所有从s到t的路径中的权重最小者;

最短路径树

解决的是单点最短路径问题;

定义:给定一幅加权有向图和一个顶点s, 以s 为起点的一棵 最短路径树是图的一幅子图,它包含s和从s可达的所有顶点。这个有向树的根结点是s,树的每条路径都是有向图中的一条最短路径;

加权有向边

Java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class DirectedEdge {
private final int v;
private final int w;
private final double weight;
/**
* Initializes a directed edge from vertex {@code v} to vertex {@code w} with
* the given {@code weight}.
* @param v the tail vertex
* @param w the head vertex
* @param weight the weight of the directed edge
* @throws IllegalArgumentException if either {@code v} or {@code w}
* is a negative integer
* @throws IllegalArgumentException if {@code weight} is {@code NaN}
*/
public DirectedEdge(int v, int w, double weight) {
if (v < 0) throw new IllegalArgumentException("Vertex names must be nonnegative integers");
if (w < 0) throw new IllegalArgumentException("Vertex names must be nonnegative integers");
if (Double.isNaN(weight)) throw new IllegalArgumentException("Weight is NaN");
this.v = v;
this.w = w;
this.weight = weight;
}
/**
* Returns the tail vertex of the directed edge.
* @return the tail vertex of the directed edge
*/
public int from() {
return v;
}
/**
* Returns the head vertex of the directed edge.
* @return the head vertex of the directed edge
*/
public int to() {
return w;
}
/**
* Returns the weight of the directed edge.
* @return the weight of the directed edge
*/
public double weight() {
return weight;
}
/**
* Returns a string representation of the directed edge.
* @return a string representation of the directed edge
*/
public String toString() {
return v + "->" + w + " " + String.format("%5.2f", weight);
}
/**
* Unit tests the {@code DirectedEdge} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
DirectedEdge e = new DirectedEdge(12, 34, 5.67);
StdOut.println(e);
}
}

加权有向图

java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
public class EdgeWeightedDigraph {
private static final String NEWLINE = System.getProperty("line.separator");
private final int V; // number of vertices in this digraph
private int E; // number of edges in this digraph
private Bag<DirectedEdge>[] adj; // adj[v] = adjacency list for vertex v
private int[] indegree; // indegree[v] = indegree of vertex v
/**
* Initializes an empty edge-weighted digraph with {@code V} vertices and 0 edges.
*
* @param V the number of vertices
* @throws IllegalArgumentException if {@code V < 0}
*/
public EdgeWeightedDigraph(int V) {
if (V < 0) throw new IllegalArgumentException("Number of vertices in a Digraph must be nonnegative");
this.V = V;
this.E = 0;
this.indegree = new int[V];
adj = (Bag<DirectedEdge>[]) new Bag[V];
for (int v = 0; v < V; v++)
adj[v] = new Bag<DirectedEdge>();
}
/**
* Initializes a random edge-weighted digraph with {@code V} vertices and <em>E</em> edges.
*
* @param V the number of vertices
* @param E the number of edges
* @throws IllegalArgumentException if {@code V < 0}
* @throws IllegalArgumentException if {@code E < 0}
*/
public EdgeWeightedDigraph(int V, int E) {
this(V);
if (E < 0) throw new IllegalArgumentException("Number of edges in a Digraph must be nonnegative");
for (int i = 0; i < E; i++) {
int v = StdRandom.uniform(V);
int w = StdRandom.uniform(V);
double weight = 0.01 * StdRandom.uniform(100);
DirectedEdge e = new DirectedEdge(v, w, weight);
addEdge(e);
}
}
/**
* Initializes an edge-weighted digraph from the specified input stream.
* The format is the number of vertices <em>V</em>,
* followed by the number of edges <em>E</em>,
* followed by <em>E</em> pairs of vertices and edge weights,
* with each entry separated by whitespace.
*
* @param in the input stream
* @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range
* @throws IllegalArgumentException if the number of vertices or edges is negative
*/
public EdgeWeightedDigraph(In in) {
this(in.readInt());
int E = in.readInt();
if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
for (int i = 0; i < E; i++) {
int v = in.readInt();
int w = in.readInt();
validateVertex(v);
validateVertex(w);
double weight = in.readDouble();
addEdge(new DirectedEdge(v, w, weight));
}
}
/**
* Initializes a new edge-weighted digraph that is a deep copy of {@code G}.
*
* @param G the edge-weighted digraph to copy
*/
public EdgeWeightedDigraph(EdgeWeightedDigraph G) {
this(G.V());
this.E = G.E();
for (int v = 0; v < G.V(); v++)
this.indegree[v] = G.indegree(v);
for (int v = 0; v < G.V(); v++) {
// reverse so that adjacency list is in same order as original
Stack<DirectedEdge> reverse = new Stack<DirectedEdge>();
for (DirectedEdge e : G.adj[v]) {
reverse.push(e);
}
for (DirectedEdge e : reverse) {
adj[v].add(e);
}
}
}
/**
* Returns the number of vertices in this edge-weighted digraph.
*
* @return the number of vertices in this edge-weighted digraph
*/
public int V() {
return V;
}
/**
* Returns the number of edges in this edge-weighted digraph.
*
* @return the number of edges in this edge-weighted digraph
*/
public int E() {
return E;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Adds the directed edge {@code e} to this edge-weighted digraph.
*
* @param e the edge
* @throws IllegalArgumentException unless endpoints of edge are between {@code 0}
* and {@code V-1}
*/
public void addEdge(DirectedEdge e) {
int v = e.from();
int w = e.to();
validateVertex(v);
validateVertex(w);
adj[v].add(e);
indegree[w]++;
E++;
}
/**
* Returns the directed edges incident from vertex {@code v}.
*
* @param v the vertex
* @return the directed edges incident from vertex {@code v} as an Iterable
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<DirectedEdge> adj(int v) {
validateVertex(v);
return adj[v];
}
/**
* Returns the number of directed edges incident from vertex {@code v}.
* This is known as the <em>outdegree</em> of vertex {@code v}.
*
* @param v the vertex
* @return the outdegree of vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int outdegree(int v) {
validateVertex(v);
return adj[v].size();
}
/**
* Returns the number of directed edges incident to vertex {@code v}.
* This is known as the <em>indegree</em> of vertex {@code v}.
*
* @param v the vertex
* @return the indegree of vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int indegree(int v) {
validateVertex(v);
return indegree[v];
}
/**
* Returns all directed edges in this edge-weighted digraph.
* To iterate over the edges in this edge-weighted digraph, use foreach notation:
* {@code for (DirectedEdge e : G.edges())}.
*
* @return all edges in this edge-weighted digraph, as an iterable
*/
public Iterable<DirectedEdge> edges() {
Bag<DirectedEdge> list = new Bag<DirectedEdge>();
for (int v = 0; v < V; v++) {
for (DirectedEdge e : adj(v)) {
list.add(e);
}
}
return list;
}
/**
* Returns a string representation of this edge-weighted digraph.
*
* @return the number of vertices <em>V</em>, followed by the number of edges <em>E</em>,
* followed by the <em>V</em> adjacency lists of edges
*/
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " " + E + NEWLINE);
for (int v = 0; v < V; v++) {
s.append(v + ": ");
for (DirectedEdge e : adj[v]) {
s.append(e + " ");
}
s.append(NEWLINE);
}
return s.toString();
}
/**
* Unit tests the {@code EdgeWeightedDigraph} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedDigraph G = new EdgeWeightedDigraph(in);
StdOut.println(G);
}
}

松弛操作 Relaxation

定义: 放松边 v -> w 意味着检查从 s(原点)到 w 的最短路径是否是先从 s 到 v,然后再由 v到w。如果是,则根据这个情况更新数据结构的内容;否则这个边失效;

1
2
3
4
5
6
7
8
int[] distTo; //distTo[v]表示从顶点s 到 v 的距离,或者说是长度;
private void relax(DirectedEdge e) {
int v = e.from(), w = e.to();
if (distTo[w] > distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weigth();
edgeTo[w] = e;
}
}

Dijkstra 算法

Dijkstra 算法能够解决边权重非负的加权有向图的单起点最短路径问题

需要的数据结构:distTo[] 、edgeTo[]和 IndexMinPQ 索引队列;

索引队列用来保存需要被放松的顶点并确认下一个被放松的顶点。

java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
public class DijkstraSP {
private double[] distTo; // distTo[v] = distance of shortest s->v path
private DirectedEdge[] edgeTo; // edgeTo[v] = last edge on shortest s->v path
private IndexMinPQ<Double> pq; // priority queue of vertices
/**
* Computes a shortest-paths tree from the source vertex {@code s} to every other
* vertex in the edge-weighted digraph {@code G}.
*
* @param G the edge-weighted digraph
* @param s the source vertex
* @throws IllegalArgumentException if an edge weight is negative
* @throws IllegalArgumentException unless {@code 0 <= s < V}
*/
public DijkstraSP(EdgeWeightedDigraph G, int s) {
for (DirectedEdge e : G.edges()) {
if (e.weight() < 0)
throw new IllegalArgumentException("edge " + e + " has negative weight");
}
distTo = new double[G.V()];
edgeTo = new DirectedEdge[G.V()];
validateVertex(s);
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
// relax vertices in order of distance from s
pq = new IndexMinPQ<Double>(G.V());
pq.insert(s, distTo[s]);
while (!pq.isEmpty()) {
int v = pq.delMin();
for (DirectedEdge e : G.adj(v))
relax(e);
}
// check optimality conditions
assert check(G, s);
}
// relax edge e and update pq if changed
private void relax(DirectedEdge e) {
int v = e.from(), w = e.to();
if (distTo[w] > distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if (pq.contains(w)) pq.decreaseKey(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
/**
* Returns the length of a shortest path from the source vertex {@code s} to vertex {@code v}.
* @param v the destination vertex
* @return the length of a shortest path from the source vertex {@code s} to vertex {@code v};
* {@code Double.POSITIVE_INFINITY} if no such path
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public double distTo(int v) {
validateVertex(v);
return distTo[v];
}
/**
* Returns true if there is a path from the source vertex {@code s} to vertex {@code v}.
*
* @param v the destination vertex
* @return {@code true} if there is a path from the source vertex
* {@code s} to vertex {@code v}; {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return distTo[v] < Double.POSITIVE_INFINITY;
}
/**
* Returns a shortest path from the source vertex {@code s} to vertex {@code v}.
*
* @param v the destination vertex
* @return a shortest path from the source vertex {@code s} to vertex {@code v}
* as an iterable of edges, and {@code null} if no such path
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<DirectedEdge> pathTo(int v) {
validateVertex(v);
if (!hasPathTo(v)) return null;
Stack<DirectedEdge> path = new Stack<DirectedEdge>();
for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
path.push(e);
}
return path;
}
// check optimality conditions:
// (i) for all edges e: distTo[e.to()] <= distTo[e.from()] + e.weight()
// (ii) for all edge e on the SPT: distTo[e.to()] == distTo[e.from()] + e.weight()
private boolean check(EdgeWeightedDigraph G, int s) {
// check that edge weights are nonnegative
for (DirectedEdge e : G.edges()) {
if (e.weight() < 0) {
System.err.println("negative edge weight detected");
return false;
}
}
// check that distTo[v] and edgeTo[v] are consistent
if (distTo[s] != 0.0 || edgeTo[s] != null) {
System.err.println("distTo[s] and edgeTo[s] inconsistent");
return false;
}
for (int v = 0; v < G.V(); v++) {
if (v == s) continue;
if (edgeTo[v] == null && distTo[v] != Double.POSITIVE_INFINITY) {
System.err.println("distTo[] and edgeTo[] inconsistent");
return false;
}
}
// check that all edges e = v->w satisfy distTo[w] <= distTo[v] + e.weight()
for (int v = 0; v < G.V(); v++) {
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (distTo[v] + e.weight() < distTo[w]) {
System.err.println("edge " + e + " not relaxed");
return false;
}
}
}
// check that all edges e = v->w on SPT satisfy distTo[w] == distTo[v] + e.weight()
for (int w = 0; w < G.V(); w++) {
if (edgeTo[w] == null) continue;
DirectedEdge e = edgeTo[w];
int v = e.from();
if (w != e.to()) return false;
if (distTo[v] + e.weight() != distTo[w]) {
System.err.println("edge " + e + " on shortest path not tight");
return false;
}
}
return true;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
int V = distTo.length;
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Unit tests the {@code DijkstraSP} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedDigraph G = new EdgeWeightedDigraph(in);
int s = Integer.parseInt(args[1]);
// compute shortest paths
DijkstraSP sp = new DijkstraSP(G, s);
// print shortest path
for (int t = 0; t < G.V(); t++) {
if (sp.hasPathTo(t)) {
StdOut.printf("%d to %d (%.2f) ", s, t, sp.distTo(t));
for (DirectedEdge e : sp.pathTo(t)) {
StdOut.print(e + " ");
}
StdOut.println();
}
else {
StdOut.printf("%d to %d no path\n", s, t);
}
}
}
}

应用:

  1. 给定两点的最短路径

  2. 任意顶点之间的最短路径

    java 实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    public class DijkstraAllPairsSP {
    private DijkstraSP[] all;
    /**
    * Computes a shortest paths tree from each vertex to to every other vertex in
    * the edge-weighted digraph {@code G}.
    * @param G the edge-weighted digraph
    * @throws IllegalArgumentException if an edge weight is negative
    * @throws IllegalArgumentException unless {@code 0 <= s < V}
    */
    public DijkstraAllPairsSP(EdgeWeightedDigraph G) {
    all = new DijkstraSP[G.V()];
    for (int v = 0; v < G.V(); v++)
    all[v] = new DijkstraSP(G, v);
    }
    /**
    * Returns a shortest path from vertex {@code s} to vertex {@code t}.
    * @param s the source vertex
    * @param t the destination vertex
    * @return a shortest path from vertex {@code s} to vertex {@code t}
    * as an iterable of edges, and {@code null} if no such path
    * @throws IllegalArgumentException unless {@code 0 <= s < V}
    * @throws IllegalArgumentException unless {@code 0 <= t < V}
    */
    public Iterable<DirectedEdge> path(int s, int t) {
    validateVertex(s);
    validateVertex(t);
    return all[s].pathTo(t);
    }
    /**
    * Is there a path from the vertex {@code s} to vertex {@code t}?
    * @param s the source vertex
    * @param t the destination vertex
    * @return {@code true} if there is a path from vertex {@code s}
    * to vertex {@code t}, and {@code false} otherwise
    * @throws IllegalArgumentException unless {@code 0 <= s < V}
    * @throws IllegalArgumentException unless {@code 0 <= t < V}
    */
    public boolean hasPath(int s, int t) {
    validateVertex(s);
    validateVertex(t);
    return dist(s, t) < Double.POSITIVE_INFINITY;
    }
    /**
    * Returns the length of a shortest path from vertex {@code s} to vertex {@code t}.
    * @param s the source vertex
    * @param t the destination vertex
    * @return the length of a shortest path from vertex {@code s} to vertex {@code t};
    * {@code Double.POSITIVE_INFINITY} if no such path
    * @throws IllegalArgumentException unless {@code 0 <= s < V}
    * @throws IllegalArgumentException unless {@code 0 <= t < V}
    */
    public double dist(int s, int t) {
    validateVertex(s);
    validateVertex(t);
    return all[s].distTo(t);
    }
    // throw an IllegalArgumentException unless {@code 0 <= v < V}
    private void validateVertex(int v) {
    int V = all.length;
    if (v < 0 || v >= V)
    throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
    }
    }

加权有向无环图的最短路径算法

思想:将顶点的放松和拓扑排序结合起来;

首先用拓扑排序得到图的所有顶点,然后放松所有顶点;

java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public class AcyclicSP {
private double[] distTo; // distTo[v] = distance of shortest s->v path
private DirectedEdge[] edgeTo; // edgeTo[v] = last edge on shortest s->v path
/**
* Computes a shortest paths tree from {@code s} to every other vertex in
* the directed acyclic graph {@code G}.
* @param G the acyclic digraph
* @param s the source vertex
* @throws IllegalArgumentException if the digraph is not acyclic
* @throws IllegalArgumentException unless {@code 0 <= s < V}
*/
public AcyclicSP(EdgeWeightedDigraph G, int s) {
distTo = new double[G.V()];
edgeTo = new DirectedEdge[G.V()];
validateVertex(s);
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
// visit vertices in topological order
Topological topological = new Topological(G);
if (!topological.hasOrder())
throw new IllegalArgumentException("Digraph is not acyclic.");
for (int v : topological.order()) {
for (DirectedEdge e : G.adj(v))
relax(e);
}
}
// relax edge e
private void relax(DirectedEdge e) {
int v = e.from(), w = e.to();
if (distTo[w] > distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
}
}
/**
* Returns the length of a shortest path from the source vertex {@code s} to vertex {@code v}.
* @param v the destination vertex
* @return the length of a shortest path from the source vertex {@code s} to vertex {@code v};
* {@code Double.POSITIVE_INFINITY} if no such path
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public double distTo(int v) {
validateVertex(v);
return distTo[v];
}
/**
* Is there a path from the source vertex {@code s} to vertex {@code v}?
* @param v the destination vertex
* @return {@code true} if there is a path from the source vertex
* {@code s} to vertex {@code v}, and {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return distTo[v] < Double.POSITIVE_INFINITY;
}
/**
* Returns a shortest path from the source vertex {@code s} to vertex {@code v}.
* @param v the destination vertex
* @return a shortest path from the source vertex {@code s} to vertex {@code v}
* as an iterable of edges, and {@code null} if no such path
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<DirectedEdge> pathTo(int v) {
validateVertex(v);
if (!hasPathTo(v)) return null;
Stack<DirectedEdge> path = new Stack<DirectedEdge>();
for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
path.push(e);
}
return path;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
int V = distTo.length;
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Unit tests the {@code AcyclicSP} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
int s = Integer.parseInt(args[1]);
EdgeWeightedDigraph G = new EdgeWeightedDigraph(in);
// find shortest path from s to each other vertex in DAG
AcyclicSP sp = new AcyclicSP(G, s);
for (int v = 0; v < G.V(); v++) {
if (sp.hasPathTo(v)) {
StdOut.printf("%d to %d (%.2f) ", s, v, sp.distTo(v));
for (DirectedEdge e : sp.pathTo(v)) {
StdOut.print(e + " ");
}
StdOut.println();
}
else {
StdOut.printf("%d to %d no path\n", s, v);
}
}
}
}

Bellman-Ford 算法

定义:在任意含有 V 个顶点的加权有向图中给定起点 s , 从 s 无法到达任何负权重环,一下算法够解决其中的单点最短路径问题:将 distTo[s] 初始化为 0, 其他 distTo[] 元素初始化为无穷大,以任意顺序放松有向图的所有边,重复 V轮;

需要的数据结构:

  1. 一条用来保存即将被放松的顶点的队列queue;
  2. 一个由顶点索引的 boolean 数组 onQ[],用来指示顶点是否已经存在于队列中,防止重复插入;

Java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
public class BellmanFordSP {
private double[] distTo; // distTo[v] = distance of shortest s->v path
private DirectedEdge[] edgeTo; // edgeTo[v] = last edge on shortest s->v path
private boolean[] onQueue; // onQueue[v] = is v currently on the queue?
private Queue<Integer> queue; // queue of vertices to relax
private int cost; // number of calls to relax()
private Iterable<DirectedEdge> cycle; // negative cycle (or null if no such cycle)
/**
* Computes a shortest paths tree from {@code s} to every other vertex in
* the edge-weighted digraph {@code G}.
* @param G the acyclic digraph
* @param s the source vertex
* @throws IllegalArgumentException unless {@code 0 <= s < V}
*/
public BellmanFordSP(EdgeWeightedDigraph G, int s) {
distTo = new double[G.V()];
edgeTo = new DirectedEdge[G.V()];
onQueue = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
// Bellman-Ford algorithm
queue = new Queue<Integer>();
queue.enqueue(s);
onQueue[s] = true;
while (!queue.isEmpty() && !hasNegativeCycle()) {
int v = queue.dequeue();
onQueue[v] = false;
relax(G, v);
}
assert check(G, s);
}
// relax vertex v and put other endpoints on queue if changed
private void relax(EdgeWeightedDigraph G, int v) {
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (distTo[w] > distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if (!onQueue[w]) {
queue.enqueue(w);
onQueue[w] = true;
}
}
if (cost++ % G.V() == 0) {
findNegativeCycle();
if (hasNegativeCycle()) return; // found a negative cycle
}
}
}
/**
* Is there a negative cycle reachable from the source vertex {@code s}?
* @return {@code true} if there is a negative cycle reachable from the
* source vertex {@code s}, and {@code false} otherwise
*/
public boolean hasNegativeCycle() {
return cycle != null;
}
/**
* Returns a negative cycle reachable from the source vertex {@code s}, or {@code null}
* if there is no such cycle.
* @return a negative cycle reachable from the soruce vertex {@code s}
* as an iterable of edges, and {@code null} if there is no such cycle
*/
public Iterable<DirectedEdge> negativeCycle() {
return cycle;
}
// by finding a cycle in predecessor graph
private void findNegativeCycle() {
int V = edgeTo.length;
EdgeWeightedDigraph spt = new EdgeWeightedDigraph(V);
for (int v = 0; v < V; v++)
if (edgeTo[v] != null)
spt.addEdge(edgeTo[v]);
EdgeWeightedDirectedCycle finder = new EdgeWeightedDirectedCycle(spt);
cycle = finder.cycle();
}
/**
* Returns the length of a shortest path from the source vertex {@code s} to vertex {@code v}.
* @param v the destination vertex
* @return the length of a shortest path from the source vertex {@code s} to vertex {@code v};
* {@code Double.POSITIVE_INFINITY} if no such path
* @throws UnsupportedOperationException if there is a negative cost cycle reachable
* from the source vertex {@code s}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public double distTo(int v) {
validateVertex(v);
if (hasNegativeCycle())
throw new UnsupportedOperationException("Negative cost cycle exists");
return distTo[v];
}
/**
* Is there a path from the source {@code s} to vertex {@code v}?
* @param v the destination vertex
* @return {@code true} if there is a path from the source vertex
* {@code s} to vertex {@code v}, and {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return distTo[v] < Double.POSITIVE_INFINITY;
}
/**
* Returns a shortest path from the source {@code s} to vertex {@code v}.
* @param v the destination vertex
* @return a shortest path from the source {@code s} to vertex {@code v}
* as an iterable of edges, and {@code null} if no such path
* @throws UnsupportedOperationException if there is a negative cost cycle reachable
* from the source vertex {@code s}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<DirectedEdge> pathTo(int v) {
validateVertex(v);
if (hasNegativeCycle())
throw new UnsupportedOperationException("Negative cost cycle exists");
if (!hasPathTo(v)) return null;
Stack<DirectedEdge> path = new Stack<DirectedEdge>();
for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
path.push(e);
}
return path;
}
// check optimality conditions: either
// (i) there exists a negative cycle reacheable from s
// or
// (ii) for all edges e = v->w: distTo[w] <= distTo[v] + e.weight()
// (ii') for all edges e = v->w on the SPT: distTo[w] == distTo[v] + e.weight()
private boolean check(EdgeWeightedDigraph G, int s) {
// has a negative cycle
if (hasNegativeCycle()) {
double weight = 0.0;
for (DirectedEdge e : negativeCycle()) {
weight += e.weight();
}
if (weight >= 0.0) {
System.err.println("error: weight of negative cycle = " + weight);
return false;
}
}
// no negative cycle reachable from source
else {
// check that distTo[v] and edgeTo[v] are consistent
if (distTo[s] != 0.0 || edgeTo[s] != null) {
System.err.println("distanceTo[s] and edgeTo[s] inconsistent");
return false;
}
for (int v = 0; v < G.V(); v++) {
if (v == s) continue;
if (edgeTo[v] == null && distTo[v] != Double.POSITIVE_INFINITY) {
System.err.println("distTo[] and edgeTo[] inconsistent");
return false;
}
}
// check that all edges e = v->w satisfy distTo[w] <= distTo[v] + e.weight()
for (int v = 0; v < G.V(); v++) {
for (DirectedEdge e : G.adj(v)) {
int w = e.to();
if (distTo[v] + e.weight() < distTo[w]) {
System.err.println("edge " + e + " not relaxed");
return false;
}
}
}
// check that all edges e = v->w on SPT satisfy distTo[w] == distTo[v] + e.weight()
for (int w = 0; w < G.V(); w++) {
if (edgeTo[w] == null) continue;
DirectedEdge e = edgeTo[w];
int v = e.from();
if (w != e.to()) return false;
if (distTo[v] + e.weight() != distTo[w]) {
System.err.println("edge " + e + " on shortest path not tight");
return false;
}
}
}
StdOut.println("Satisfies optimality conditions");
StdOut.println();
return true;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
int V = distTo.length;
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
/**
* Unit tests the {@code BellmanFordSP} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
int s = Integer.parseInt(args[1]);
EdgeWeightedDigraph G = new EdgeWeightedDigraph(in);
BellmanFordSP sp = new BellmanFordSP(G, s);
// print negative cycle
if (sp.hasNegativeCycle()) {
for (DirectedEdge e : sp.negativeCycle())
StdOut.println(e);
}
// print shortest paths
else {
for (int v = 0; v < G.V(); v++) {
if (sp.hasPathTo(v)) {
StdOut.printf("%d to %d (%5.2f) ", s, v, sp.distTo(v));
for (DirectedEdge e : sp.pathTo(v)) {
StdOut.print(e + " ");
}
StdOut.println();
}
else {
StdOut.printf("%d to %d no path\n", s, v);
}
}
}
}
}