Commit 12947966 authored by Fokion Zervoudakis's avatar Fokion Zervoudakis

Use DFS to detect cycles.

parent 22a132bd
......@@ -30,32 +30,32 @@ class AdjMatrix<T extends Vertex> implements Graph<T> {
@Override
public void addVertex(T u) {
M[u.key][u.key] = u;
M[u.getKey()][u.getKey()] = u;
}
@Override
public void addEdge(T u, T v) {
addVertex(u);
addVertex(v);
M[u.key][v.key] = v;
M[u.getKey()][v.getKey()] = v;
}
@Override
public void removeEdge(T u, T v) {
M[u.key][v.key] = null;
M[u.getKey()][v.getKey()] = null;
}
@Override
public boolean hasEdge(T u, T v) {
return M[u.key][v.key] != null;
return M[u.getKey()][v.getKey()] != null;
}
@Override
public List<T> inEdges(T u) {
var L = new ArrayList<T>();
for (int i = 0; i < n; i++) {
if (i != u.key) {
var v = M[i][u.key];
if (i != u.getKey()) {
var v = M[i][u.getKey()];
if (v != null) {
L.add(M[i][i]);
}
......@@ -68,8 +68,8 @@ class AdjMatrix<T extends Vertex> implements Graph<T> {
public List<T> outEdges(T u) {
var L = new ArrayList<T>();
for (int j = 0; j < n; j++) {
if (j != u.key) {
var v = M[u.key][j];
if (j != u.getKey()) {
var v = M[u.getKey()][j];
if (v != null) {
L.add(v);
}
......
......@@ -27,12 +27,26 @@ class Bfs {
Vertex u = Q.remove(0);
for (Vertex v : G.outEdges(u)) {
if (!v.visited) {
v.setParent(u);
v.d = u.d + 1;
v.p = u;
v.visited = true;
Q.add(v);
}
}
}
}
static class Vertex extends graph.Vertex {
int d = 0;
boolean visited = false;
Vertex(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
}
package graph;
import java.util.ArrayList;
import java.util.List;
import static graph.Color.BLACK;
import static graph.Color.WHITE;
class Bfs2 {
boolean isBipartite(Graph<Vertex> G, Vertex start) {
List<Vertex> Q = new ArrayList<>();
Q.add(start);
start.visited = true;
start.color = WHITE;
while (!Q.isEmpty()) {
Vertex u = Q.remove(0);
for (Vertex v : G.outEdges(u)) {
if (v.visited || v.color == u.color) {
return false;
} else {
v.setParent(u);
v.d = u.d + 1;
v.visited = true;
v.color = u.complement();
Q.add(v);
}
}
}
return true;
}
static class Vertex extends graph.Vertex {
int d = 0;
boolean visited = false;
Color color;
Vertex(int key) {
super(key);
}
Color complement() {
return (color == WHITE) ? BLACK : WHITE;
}
@Override
public String toString() {
return super.toString() + ":" + color + ":" + d;
}
}
}
package graph;
enum Color {
WHITE, GRAY, BLACK
}
package graph;
import java.util.ArrayList;
import java.util.List;
class Coloring {
class Bfs {
boolean isBipartite(Graph<Vertex> G, Vertex start) {
List<Vertex> Q = new ArrayList<>();
Q.add(start);
start.visited = true;
start.c = Color.WH;
while (!Q.isEmpty()) {
Vertex u = Q.remove(0);
for (Vertex v : G.outEdges(u)) {
if (v.visited || v.c == u.c) {
return false;
} else {
v.d = u.d + 1;
v.p = u;
v.c = complement(u.c);
v.visited = true;
Q.add(v);
}
}
}
return true;
}
}
private Color complement(Color c) {
return (c == Color.WH) ? Color.BK : Color.WH;
}
enum Color {
BK, WH
}
static class Vertex extends graph.Vertex {
Color c;
Vertex(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + c;
}
}
}
......@@ -6,8 +6,8 @@ class Dfs {
class It {
/**
Uses iterative depth-first search backed by a last-in-first-out data
structure to detect cycles, and topologically sort all vertices, in a
directed acyclic graph {@code G=(V,E)}.
structure to topologically sort all vertices in a directed acyclic graph
{@code G=(V,E)}.
<ul>
<li>time_worst=O(V+E)
<li>space_worst=O(V)
......@@ -31,7 +31,7 @@ class Dfs {
u.visited = true;
for (Vertex v : G.outEdges(u)) {
if (!v.visited) {
v.p = u;
v.setParent(u);
S.push(v);
}
}
......@@ -41,8 +41,8 @@ class Dfs {
class Rec {
/**
Uses recursive depth-first search to detect cycles, and topologically
sort all vertices, in a directed acyclic graph {@code G=(V,E)}.
Uses recursive depth-first search to topologically sort all vertices in
a directed acyclic graph {@code G=(V,E)}.
<ul>
<li>time_worst=O(V+E)
<li>space_worst=O(V)
......@@ -62,10 +62,18 @@ class Dfs {
u.visited = true;
for (Vertex v : G.outEdges(u)) {
if (!v.visited) {
v.p = u;
v.setParent(u);
dfsVisit(G, v);
}
}
}
}
static class Vertex extends graph.Vertex {
boolean visited = false;
Vertex(int key) {
super(key);
}
}
}
package graph;
import static graph.Color.BLACK;
import static graph.Color.GRAY;
import static graph.Color.WHITE;
class Dfs2 {
/**
Uses recursive depth-first search to detect cycles in a directed acyclic
graph {@code G=(V,E)}. In the absence of cycles, all vertices in {@code G}
are sorted topologically.
<ul>
<li>time_worst=O(V+E)
<li>space_worst=O(V)
</ul>
@param G a graph
*/
void dfs(Graph<Vertex> G, Vertex u) {
u.color = GRAY;
for (Vertex v : G.outEdges(u)) {
if (v.color == GRAY) {
throw new RuntimeException();
} else if (v.color == WHITE) {
v.setParent(u);
v.color = GRAY;
dfs(G, v);
}
}
u.color = BLACK;
}
static class Vertex extends graph.Vertex {
Color color = WHITE;
Vertex(int key) {
super(key);
}
}
}
......@@ -4,10 +4,10 @@ class Path {
static String print(Vertex src, Vertex dest) {
if (dest.equals(src)) {
return dest + "";
} else if (dest.p == null) {
} else if (dest.getParent() == null) {
return null;
} else {
return print(src, dest.p) + " -> " + dest;
return print(src, dest.getParent()) + " -> " + dest;
}
}
}
package graph;
class Vertex {
int d = 0;
boolean visited = false;
private int key;
Vertex p;
private Vertex parent;
int key;
Vertex() {
}
Vertex(int key) {
this.key = key;
}
int getKey() {
return key;
}
Vertex getParent() {
return parent;
}
void setParent(Vertex v) {
this.parent = v;
}
@Override
public String toString() {
return key + "";
......
......@@ -6,26 +6,26 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ColoringTest_AdjList_1 {
private Graph<Coloring.Vertex> G;
private Coloring.Vertex v0;
private Coloring.Bfs bfs;
class Bfs2Test_AdjList_1 {
private Graph<Bfs2.Vertex> G;
private Bfs2.Vertex v0;
private Bfs2 bfs;
@BeforeEach
void beforeEach() {
v0 = new Stub(0);
v0 = new Bfs2.Vertex(0);
G = new AdjList<>();
G.addVertex(v0);
bfs = new Coloring().new Bfs();
bfs = new Bfs2();
}
@Test
void itSearchesOneVertexWithZeroNeighbors() {
assertTrue(bfs.isBipartite(G, v0));
var expected = "{0:WH:0=[]}";
var expected = "{0:WHITE:0=[]}";
var actual = G.toString();
assertEquals(expected, actual);
......@@ -33,11 +33,11 @@ class ColoringTest_AdjList_1 {
@Test
void itSearchesOneVertexWithOneNeighbor() {
G.addEdge(v0, new Stub(1));
G.addEdge(v0, new Bfs2.Vertex(1));
assertTrue(bfs.isBipartite(G, v0));
var expected = "{0:WH:0=[1:BK:1], 1:BK:1=[]}";
var expected = "{0:WHITE:0=[1:BLACK:1], 1:BLACK:1=[]}";
var actual = G.toString();
assertEquals(expected, actual);
......@@ -45,27 +45,14 @@ class ColoringTest_AdjList_1 {
@Test
void itSearchesOneVertexWithManyNeighbors() {
G.addEdge(v0, new Stub(1));
G.addEdge(v0, new Stub(2));
G.addEdge(v0, new Bfs2.Vertex(1));
G.addEdge(v0, new Bfs2.Vertex(2));
assertTrue(bfs.isBipartite(G, v0));
var expected = "{0:WH:0=[1:BK:1, 2:BK:1], 1:BK:1=[], 2:BK:1=[]}";
var expected = "{0:WHITE:0=[1:BLACK:1, 2:BLACK:1], 1:BLACK:1=[], 2:BLACK:1=[]}";
var actual = G.toString();
assertEquals(expected, actual);
}
//<editor-fold desc="stubs">
class Stub extends Coloring.Vertex {
Stub(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
//</editor-fold>
}
......@@ -6,16 +6,16 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
class ColoringTest_AdjList_2 {
private Graph<Coloring.Vertex> G;
private Coloring.Vertex v0;
private Coloring.Bfs bfs;
class Bfs2Test_AdjList_2 {
private Graph<Bfs2.Vertex> G;
private Bfs2.Vertex v0;
private Bfs2 bfs;
@BeforeEach
void beforeEach() {
v0 = new Stub(0);
v0 = new Bfs2.Vertex(0);
G = new AdjList<>();
bfs = new Coloring().new Bfs();
bfs = new Bfs2();
}
@Test
......@@ -25,7 +25,7 @@ class ColoringTest_AdjList_2 {
assertFalse(bfs.isBipartite(G, v0));
var expected = "{0:WH:0=[0:WH:0]}";
var expected = "{0:WHITE:0=[0:WHITE:0]}";
var actual = G.toString();
assertEquals(expected, actual);
......@@ -33,7 +33,7 @@ class ColoringTest_AdjList_2 {
@Test
void itIgnoresCyclesFromSymmetricPaths() {
var v1 = new Stub(1);
var v1 = new Bfs2.Vertex(1);
// v0 -> v1 -> v0
G.addEdge(v0, v1);
......@@ -41,7 +41,7 @@ class ColoringTest_AdjList_2 {
assertFalse(bfs.isBipartite(G, v0));
var expected = "{0:WH:0=[1:BK:1], 1:BK:1=[0:WH:0]}";
var expected = "{0:WHITE:0=[1:BLACK:1], 1:BLACK:1=[0:WHITE:0]}";
var actual = G.toString();
assertEquals(expected, actual);
......@@ -49,8 +49,8 @@ class ColoringTest_AdjList_2 {
@Test
void itIgnoresCyclesFromAsymmetricPaths() {
var v1 = new Stub(1);
var v2 = new Stub(2);
var v1 = new Bfs2.Vertex(1);
var v2 = new Bfs2.Vertex(2);
// v0 -> v1 -> v2 -> v0
G.addEdge(v0, v1);
......@@ -59,22 +59,9 @@ class ColoringTest_AdjList_2 {
assertFalse(bfs.isBipartite(G, v0));
var expected = "{0:WH:0=[1:BK:1], 1:BK:1=[2:WH:2], 2:WH:2=[0:WH:0]}";
var expected = "{0:WHITE:0=[1:BLACK:1], 1:BLACK:1=[2:WHITE:2], 2:WHITE:2=[0:WHITE:0]}";
var actual = G.toString();
assertEquals(expected, actual);
}
//<editor-fold desc="stubs">
class Stub extends Coloring.Vertex {
Stub(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
//</editor-fold>
}
......@@ -5,18 +5,18 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
class ColoringTest_AdjList_3 {
class Bfs2Test_AdjList_3 {
@Test
void itSearchesManyVerticesWithManyNeighbors() {
var v0 = new Stub(0);
var v1 = new Stub(1);
var v2 = new Stub(2);
var v3 = new Stub(3);
var v4 = new Stub(4);
var v5 = new Stub(5);
var v6 = new Stub(6);
var v0 = new Bfs2.Vertex(0);
var v1 = new Bfs2.Vertex(1);
var v2 = new Bfs2.Vertex(2);
var v3 = new Bfs2.Vertex(3);
var v4 = new Bfs2.Vertex(4);
var v5 = new Bfs2.Vertex(5);
var v6 = new Bfs2.Vertex(6);
var G = new AdjList<Coloring.Vertex>();
var G = new AdjList<Bfs2.Vertex>();
G.addEdge(v0, v1);
G.addEdge(v0, v2);
......@@ -33,18 +33,18 @@ class ColoringTest_AdjList_3 {
G.addEdge(v1, v0); // ignored symmetric path: v0 -> v1 -> v0
G.addEdge(v6, v0); // ignored asymmetric path: v0 -> v2 -> v6 -> v0
assertFalse(new Coloring().new Bfs().isBipartite(G, v0));
assertFalse(new Bfs2().isBipartite(G, v0));
//@formatter:off
var expected =
"{" +
"0:WH:0=[1:BK:1, 2:BK:1, 4:BK:1, 0:WH:0], " +
"1:BK:1=[3:null:0, 5:null:0, 0:WH:0], " +
"2:BK:1=[6:null:0], " +
"4:BK:1=[5:null:0], " +
"0:WHITE:0=[1:BLACK:1, 2:BLACK:1, 4:BLACK:1, 0:WHITE:0], " +
"1:BLACK:1=[3:null:0, 5:null:0, 0:WHITE:0], " +
"2:BLACK:1=[6:null:0], " +
"4:BLACK:1=[5:null:0], " +
"3:null:0=[], " +
"5:null:0=[], " +
"6:null:0=[0:WH:0]" +
"6:null:0=[0:WHITE:0]" +
"}";
//@formatter:on
......@@ -53,16 +53,4 @@ class ColoringTest_AdjList_3 {
assertEquals(expected, actual);
}
//<editor-fold desc="stubs">
class Stub extends Coloring.Vertex {
Stub(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
//</editor-fold>
}
......@@ -6,13 +6,13 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class BfsTest_AdjList_1 {
private Graph<Vertex> G;
private Vertex v0;
private Graph<Bfs.Vertex> G;
private Bfs.Vertex v0;
private Bfs bfs;
@BeforeEach
void beforeEach() {
v0 = new Stub(0);
v0 = new Bfs.Vertex(0);
G = new AdjList<>();
G.addVertex(v0);
......@@ -32,7 +32,7 @@ class BfsTest_AdjList_1 {
@Test
void itSearchesOneVertexWithOneNeighbor() {
G.addEdge(v0, new Stub(1));
G.addEdge(v0, new Bfs.Vertex(1));
bfs.bfs(G, v0);
......@@ -44,8 +44,8 @@ class BfsTest_AdjList_1 {
@Test
void itSearchesOneVertexWithManyNeighbors() {
G.addEdge(v0, new Stub(1));
G.addEdge(v0, new Stub(2));
G.addEdge(v0, new Bfs.Vertex(1));
G.addEdge(v0, new Bfs.Vertex(2));
bfs.bfs(G, v0);
......@@ -54,17 +54,4 @@ class BfsTest_AdjList_1 {
assertEquals(expected, actual);
}
//<editor-fold desc="stubs">
class Stub extends Vertex {
Stub(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
//</editor-fold>
}
......@@ -6,13 +6,13 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class BfsTest_AdjList_2 {
private Graph<Vertex> G;
private Vertex v0;
private Graph<Bfs.Vertex> G;
private Bfs.Vertex v0;
private Bfs bfs;
@BeforeEach
void beforeEach() {
v0 = new Stub(0);
v0 = new Bfs.Vertex(0);
G = new AdjList<>();
bfs = new Bfs();
}
......@@ -32,7 +32,7 @@ class BfsTest_AdjList_2 {
@Test
void itIgnoresCyclesFromSymmetricPaths() {
var v1 = new Stub(1);
var v1 = new Bfs.Vertex(1);
// v0 -> v1 -> v0
G.addEdge(v0, v1);
......@@ -48,8 +48,8 @@ class BfsTest_AdjList_2 {
@Test
void itIgnoresCyclesFromAsymmetricPaths() {
var v1 = new Stub(1);
var v2 = new Stub(2);
var v1 = new Bfs.Vertex(1);
var v2 = new Bfs.Vertex(2);
// v0 -> v1 -> v2 -> v0
G.addEdge(v0, v1);
......@@ -63,17 +63,4 @@ class BfsTest_AdjList_2 {
assertEquals(expected, actual);
}
//<editor-fold desc="stubs">
class Stub extends Vertex {
Stub(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
//</editor-fold>
}
......@@ -7,15 +7,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class BfsTest_AdjList_3 {
@Test
void itSearchesManyVerticesWithManyNeighbors() {
var v0 = new Stub(0);
var v1 = new Stub(1);
var v2 = new Stub(2);
var v3 = new Stub(3);
var v4 = new Stub(4);
var v5 = new Stub(5);
var v6 = new Stub(6);
var v0 = new Bfs.Vertex(0);
var v1 = new Bfs.Vertex(1);
var v2 = new Bfs.Vertex(2);
var v3 = new Bfs.Vertex(3);
var v4 = new Bfs.Vertex(4);
var v5 = new Bfs.Vertex(5);
var v6 = new Bfs.Vertex(6);
var G = new AdjList<>();
var G = new AdjList<Bfs.Vertex>();
G.addEdge(v0, v1);
G.addEdge(v0, v2);
......@@ -51,17 +51,4 @@ class BfsTest_AdjList_3 {
assertEquals(expected, actual);
}
//<editor-fold desc="stubs">
class Stub extends Vertex {
Stub(int key) {
super(key);
}
@Override
public String toString() {
return super.toString() + ":" + d;
}
}
//</editor-fold>
}
......@@ -6,13 +6,13 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class BfsTest_AdjMatrix_1 {
private Graph<Vertex> G;