-
Notifications
You must be signed in to change notification settings - Fork 1
/
DFS.java
380 lines (303 loc) · 10 KB
/
DFS.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
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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
package rsn170330.lp4;
import rbk.Graph;
import rbk.Graph.Vertex;
import rbk.Graph.Edge;
import rbk.Graph.GraphAlgorithm;
import rbk.Graph.Factory;
import java.io.File;
import java.util.List;
import java.util.Queue;
import java.util.LinkedList;
import java.util.Scanner;
/**
* CS 5V81.001: Implementation of Data Structures and Algorithms
* Graphs: DFS: Strongly Connected Components - Short Project 10.
*
* @author Rahul Nalawade (rsn170330)
*/
/**
* Implement the algorithm to find strongly connected components of a directed graph.
* Add the method to your DFS class from SP8. Make changes so that all methods share
* as much of the code as possible.
*
* public static DFS stronglyConnectedComponents(Graph g) { ... }
*/
public class DFS extends GraphAlgorithm<DFS.DFSVertex> {
private int cno; // Component no of the DFS graph
private int topNum; // topNum = No of Vertices = |V|
private LinkedList<Vertex> finishList; // to store topological ordering
private boolean isCyclic;
public static class DFSVertex implements Factory {
private int cno; // Component no of this node
private boolean seen; // flag to check if the node is seen or not
Color vColor; // Not used, for reference
private int top; // the order number in which we visit this node
private Vertex parent; // parent of this node
private int inDegrees; // number of inDegrees of this node
// default constructor
public DFSVertex(Vertex u) {
seen = false;
vColor = Color.WHITE; // Not used, for reference
setParent(null);
top = 0;
inDegrees = 0;
cno = 0;
}
public DFSVertex make(Vertex u) {
return new DFSVertex(u);
}
// parent getter
public Vertex getParent() {
return parent;
}
// parent setter
public void setParent(Vertex parent) {
this.parent = parent;
}
// Not used, just for reference
public enum Color {
WHITE, GRAY, BLACK;
}
}
// default constructor
// NOTE: setting it private because we don't need to make a public handle
// for some methods (like connectedComponents()) which need to run DFS before
// they are called.
private DFS(Graph g) {
super(g, new DFSVertex(null));
topNum = g.size();
finishList = new LinkedList<>();
cno = 0;
setCyclic(false);
}
/**
* Find strongly connected components of a DIRECTED graph.
* @param g the input graph
* @return the DFS object containing with updated relevant attributes
*/
public static DFS stronglyConnectedComponents(Graph g) {
DFS d = new DFS(g);
d.dfs(g);
List<Vertex> list = d.finishList;
//System.out.println(Arrays.toString(list.toArray()));
g.reverseGraph();
d.dfs(list);
g.reverseGraph();
return d;
}
/**
* Run Depth-First-Search algorithm on Graph g using method 1
* @param g the graph as input
* @return DFS object
*/
public static DFS depthFirstSearch(Graph g) {
DFS d = new DFS(g);
d.dfs(g);
return d;
}
/**
* Runs Depth-First-Search on g as well as finishList, in a Generic way.
* Keeping all other valid usages from SP08.
*
* @param iterable the Iterable collection of type Vertex
*/
private void dfs(Iterable<Vertex> iterable) {
// No of Vertices in g
topNum = g.size();
for (Vertex u: g) {
get(u).seen = false;
get(u).vColor = DFSVertex.Color.WHITE; // Not used, for reference
get(u).setParent(null);
}
finishList = new LinkedList<>();
cno = 0; // NOTE: class variable
for (Vertex u: iterable) {
//if (get(u).vColor == DFSVertex.Color.WHITE) {
if (!get(u).seen) {
cno++;
dfsVisit(u);
}
}
}
/**
* Helper method which recursively visits the nodes in DFS manner
* @param u giving a visit to node 'u'
*/
private void dfsVisit(Vertex u) {
get(u).vColor = DFSVertex.Color.GRAY;
get(u).seen = true;
get(u).cno = cno; // updating using class variable
//System.out.print(" -> "+u+"("+get(u).top+")"); // uncomment to trace()*
for (Edge e: g.incident(u)) {
Vertex v = e.otherEnd(u);
//if (get(v).vColor == DFSVertex.Color.WHITE) {
if (!get(v).seen) {
// Visited the node which is 'UnVisited' (or WHITE, v.top = 0)
get(v).setParent(u);
dfsVisit(v);
}
// When get(u).vColor == DFSVertex.Color.GRAY
else if (get(u).top > get(v).top) {
// Visited the node which is in 'Visiting' (or GRAY) status
// throw new Exception("Graph is not a DAG, as Edge ("+u+", "+v+") is a back edge.");
setCyclic(true); // SHOULD NEVER THROW AN EXCEPTION. WORKING SILENTLY.
}
// When get(u).vColor == DFSVertex.Color.BLACK
else {
// Visited the node which was 'Visited' (or BLACK, v.top > u.top)
// System.out.println("\nVertex "+v+" is already visited."); // uncomment to trace()*
}
}
// top: the number of VISITED node.
// E.g. if u.top = |V| u is the first node which was done in DFS algorithm
get(u).top = topNum--;
finishList.addFirst(u); // same as finishList.addFirst(u)
// DEFINE finishList as LinkedList (not a List)
get(u).vColor = DFSVertex.Color.BLACK;
//System.out.print(" <- "+u+"("+get(u).top+")"); // uncomment to trace()*
}
/**
* Method to update finishList which stores the nodes visited
* in increasing order of inDegrees (using method 2) FROM CLASS NOTES
* @param g the input graph
*/
private void topologicalOrdering2(Graph g) {
Queue<Vertex> zeroQ = new LinkedList<>();
for (Vertex u: g) {
get(u).inDegrees = u.inDegree();
if (get(u).inDegrees == 0) {
zeroQ.add(u);
}
}
// No of nodes that have been processed
int count = 0;
while (!zeroQ.isEmpty()) {
Vertex u = zeroQ.remove();
get(u).top = ++count;
finishList.add(u);
for (Edge e: g.incident(u)) {
Vertex v = e.otherEnd(u);
get(v).inDegrees--; // virtually deleting the edge e
if (get(v).inDegrees == 0)
zeroQ.add(v);
}
}
if (count != g.size())
setCyclic(true);
}
/**
* Find the number of connected components of a DAG by running DFS.
*
* Enter the component number of each vertex u in u.cno.
* Note that the graph g is available as a class field via GraphAlgorithm.
* @return total number of components
*/
public int connectedComponents() {
return cno;
}
/**
* Precondition: Run strongly connected components, or
* connected components algorithm.
*
* Returns Component no of vertex u.
* @param u the vertex
* @return component number of vertex u
*/
public int cno(Vertex u) {
return get(u).cno;
}
/**
* Find topological oder of a DAG using DFS method 1.
* @param g the input graph
* @return the List of vertices in order of DFS, if not DAG return null
*/
public static List<Vertex> topologicalOrder1(Graph g) {
DFS d = depthFirstSearch(g);
// Ran DFS and then giving the order
return d.topologicalOrder1();
}
// Member function to find topological order using method 1
public List<Vertex> topologicalOrder1() {
// When graph is not a DAG
if (isCyclic())
return null;
return this.finishList;
}
/**
* Find topological order of a DAG using the second algorithm.
* @param g the input graph
* @return the List of vertices in order of DFS, if not DAG return null
*/
public static List<Vertex> topologicalOrder2(Graph g) {
DFS d = new DFS(g);
// Calls method 2 which gives topological ordering
d.topologicalOrdering2(g);
return d.topologicalOrder2();
}
// Member function to find topological order using method 2
public List<Vertex> topologicalOrder2() {
// When graph is not a DAG
if (isCyclic())
return null;
return this.finishList;
}
// Can be queried for a graph which was supposed to be DAG
public boolean isCyclic() {
return isCyclic;
}
// setter method
public void setCyclic(boolean isCyclic) {
this.isCyclic = isCyclic;
}
// --------------------------- MAIN METHOD -------------------------------
public static void main(String[] args) throws Exception {
//String string = "7 6 1 2 2 1 3 3 2 4 5 3 4 4 4 5 1 6 7 1 0";
// Graph from Class Notes
String string = "11 17 1 11 1 2 3 1 2 7 1 3 10 1 4 1 1 4 9 1 5 4 1 "+
"5 7 1 5 8 1 6 3 1 7 8 1 8 2 1 9 11 1 10 6 1 11 3 1 11 4 1 11 6 1 0";
Scanner in;
// If there is a command line argument, use it as file from which
// input is read, otherwise use input from string.
in = args.length > 0 ? new Scanner(new File(args[0])) : new Scanner(string);
// Read graph from input
Graph g = Graph.readDirectedGraph(in);
// ------------------- Strongly Connected Components (SP10: Task 1) -----------------
DFS dSCC = DFS.stronglyConnectedComponents(g);
int numSCC = dSCC.connectedComponents();
System.out.println("# SP10 - STRONGLY CONNECTED COMPONENTS: ");
g.printGraph(false);
System.out.println("Number of components: " + numSCC + "\nu\tcno");
for (Vertex u: g) {
System.out.println(u + "\t" + dSCC.cno(u));
}
// --------------------------- NEW GRAPH (DAG) --------------------------------------
string = "10 12 1 3 1 1 8 1 6 8 1 6 10 1 3 2 1 8 2 1 8 5 1 5 10 1 "+
"2 4 1 5 4 1 4 7 1 10 9 1 0";
in = new Scanner(string);
// Read graph from input
g = Graph.readDirectedGraph(in);
System.out.println("\n# SP08 - CONNECTED COMPONENTS AND TOPOLOGICAL ORDERINGS: ");
// ----------------------- Connected Components (SP08: Task 3) ----------------------
DFS d = DFS.depthFirstSearch(g);
int numcc = d.connectedComponents();
g.printGraph(false);
System.out.println("Number of components: " + numcc + "\nu\tcno");
for (Vertex u: g) {
System.out.println(u + "\t" + d.cno(u));
}
// ----------------------- Topological Order 1 (SP08: Task 1) -----------------------
List<Vertex> tOrder = DFS.topologicalOrder1(g);
System.out.println("\nTopological Ordering 1: ");
for (Vertex u: tOrder) {
System.out.print(u + " ");
}
System.out.println();
// ----------------------- Topological Order 2 (SP08: Task 2) -----------------------
tOrder = DFS.topologicalOrder2(g);
System.out.println("\nTopological Ordering 2: ");
for (Vertex u: tOrder) {
System.out.print(u + " ");
}
System.out.println();
}
}