# [falling in love with data structures and algorithms Season 2] [04] Figure - basic implementation_ Traversal_ Topological sorting

Posted by firedrop84 on Fri, 04 Mar 2022 12:27:07 +0100

Continuous learning & continuous updating

Learning attitude: down to earth

The traditional adjacency matrix or adjacency table is not used in the code implementation diagram, because they are too complex and troublesome.
The code implementation adopts a compromise scheme, which is more inclined to the adjacency table.

When the code implements a graph, it is generally implemented as a directed graph, because an undirected graph can be expressed by a directed graph.

## Implementation scheme of graph

The adjacency table only needs a one-dimensional array

## Basic interface of figure

```public interface Graph<V, E> {
int vertexSize(); // Number of vertices
int edgeSize(); // Number of edges

void removeVertex(V v); // Delete a vertex

void addEdge(V from, V to, E weight); // Add an edge (weighted)
void removeEdge(V from, V to); // Delete an edge
}
```

## Definition of vertex and edge

```public class ListGraph<V, E> implements Graph<V, E> {
// vertex
private static class Vertex<V, E> {
V value; // Vertex stored elements
Set<Edge<V, E>> inEdges = new HashSet<>(); // The edge that ends at the vertex (the edge that reaches the vertex)
Set<Edge<V, E>> outEdges = new HashSet<>(); // Edge starting from this vertex (edge starting from this vertex)

Vertex(V value) {
this.value = value;
}

@Override
public boolean equals(Object o) {
Vertex<V, E> vertex = (Vertex<V, E>) o;
return Objects.equals(value, vertex.value);
}

@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}

@Override
public String toString() {
return value == null ? "null" : value.toString();
}
}

// edge
private static class Edge<V, E> {
E weight; // Weight of edge
Vertex<V, E> from; // Which vertex does this edge start from
Vertex<V, E> to; // Which vertex does this edge reach

Edge(Vertex<V, E> from, Vertex<V, E> to, E weight) {
this.from = from;
this.to = to;
this.weight = weight;
}

@Override
public boolean equals(Object o) {
Edge<V, E> edge = (Edge<V, E>) o;
return from.equals(edge.from) && to.equals(edge.to);
}

@Override
public int hashCode() {
int result = 0;
result = 31 * result + from.hashCode();
result = 31 * result + to.hashCode();
return result;
}

@Override
public String toString() {
return "Edge{" +
"from=" + from +
", to=" + to +
", weight=" + weight +
'}';
}
}
}
```

## Basic implementation of graph

```public class ListGraph<V, E> implements Graph<V, E> {
private final Map<V, Vertex<V, E>> vertices = new HashMap<>();
private final Set<Edge<V, E>> edges = new HashSet<>();

public void print() {
System.out.println("Vertex:");
vertices.forEach((k, v) -> {
System.out.println(k);
System.out.println("in:");
System.out.println(v.inEdges);
System.out.println("out:");
System.out.println(v.outEdges);
System.out.println("---------------------");
});
System.out.println("Edge:");
edges.forEach(System.out::println);
}

@Override
public int vertexSize() {
return vertices.size();
}

@Override
public int edgeSize() {
return edges.size();
}

@Override
if (vertices.containsKey(v)) return;
vertices.put(v, new Vertex<>(v));
}

@Override
public void removeVertex(V v) {
//        Vertex<V, E> vertex = vertices.get(v);
//        if (null == vertex) return;
final Vertex<V, E> removeVertex = vertices.remove(v);
if (null == removeVertex) return;

for (Iterator<Edge<V, E>> iterator = removeVertex.outEdges.iterator(); iterator.hasNext(); ) {
final Edge<V, E> edge = iterator.next();
edges.remove(edge);
edge.to.inEdges.remove(edge);
//            iterator.remove();
}
//        removeVertex.outEdges.clear();
removeVertex.outEdges = null;
for (Iterator<Edge<V, E>> iterator = removeVertex.inEdges.iterator(); iterator.hasNext(); ) {
final Edge<V, E> edge = iterator.next();
edges.remove(edge);
edge.from.outEdges.remove(edge);
//            iterator.remove();
}
//        removeVertex.inEdges.clear();
removeVertex.inEdges = null;
}

@Override
public void addEdge(V from, V to) {
}

// If you find that a vertex does not exist, you need to create it
@Override
public void addEdge(V from, V to, E weight) {
Vertex<V, E> fromVertex = vertices.get(from);
Vertex<V, E> toVertex = vertices.get(to);
if (fromVertex == null) {
fromVertex = new Vertex<>(from);
vertices.put(from, fromVertex);
}
if (toVertex == null) {
toVertex = new Vertex<>(to);
vertices.put(to, toVertex);
}
Edge<V, E> edge = new Edge<>(fromVertex, toVertex, weight);
if (fromVertex.outEdges.remove(edge)) { // If this edge already exists
toVertex.inEdges.remove(edge);
edges.remove(edge);
}
}

@Override
public void removeEdge(V from, V to) {
Vertex<V, E> fromVertex = vertices.get(from);
Vertex<V, E> toVertex = vertices.get(to);
if (fromVertex == null || toVertex == null) {
return;
}
Edge<V, E> edge = new Edge<>(fromVertex, toVertex, null);
if (edges.remove(edge)) { // If this edge exists, it needs to be deleted
toVertex.inEdges.remove(edge);
fromVertex.outEdges.remove(edge);
}
}

// vertex
private static class Vertex<V, E> {
V value; // Vertex stored elements
Set<Edge<V, E>> inEdges = new HashSet<>(); // The edge that ends at the vertex (the edge that reaches the vertex)
Set<Edge<V, E>> outEdges = new HashSet<>(); // Edge starting from this vertex (edge starting from this vertex)

Vertex(V value) {
this.value = value;
}

@Override
public boolean equals(Object o) {
Vertex<V, E> vertex = (Vertex<V, E>) o;
return Objects.equals(value, vertex.value);
}

@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}

@Override
public String toString() {
return value == null ? "null" : value.toString();
}
}

// edge
private static class Edge<V, E> {
E weight; // Weight of edge
Vertex<V, E> from; // Which vertex does this edge start from
Vertex<V, E> to; // Which vertex does this edge reach

Edge(Vertex<V, E> from, Vertex<V, E> to, E weight) {
this.from = from;
this.to = to;
this.weight = weight;
}

@Override
public boolean equals(Object o) {
Edge<V, E> edge = (Edge<V, E>) o;
return from.equals(edge.from) && to.equals(edge.to);
}

@Override
public int hashCode() {
int result = 0;
result = 31 * result + from.hashCode();
result = 31 * result + to.hashCode();
return result;
}

@Override
public String toString() {
return "Edge{" +
"from=" + from +
", to=" + to +
", weight=" + weight +
'}';
}
}
}
```

## Traversal of Graphs

When using breadth first search to traverse a graph, the traversal results are different from different vertices (it should be noted that some vertices can not be traversed sometimes)

```    // Breadth first search
@Override
public void bfs(V begin) {
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;

Queue<Vertex<V, E>> queue = new LinkedList<>();
Set<Vertex<V, E>> set = new HashSet<>();
queue.offer(beginVertex);

while (!queue.isEmpty()) {
final Vertex<V, E> vertex = queue.poll();

System.out.println(vertex); // The traversal here is simply to print the vertices

for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
queue.offer(edge.to);
}
}
}
```

## Depth first search

When using the depth first search to traverse the graph, starting from different vertices, the traversal results will be different as the breadth first search (it should be noted that some vertices can not be traversed sometimes);

When using depth first search to traverse the graph, there will be many paths to traverse from the same vertex.

```    // Depth first search -- recursive implementation
@Override
public void dfs(V begin) {
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;
dfs(beginVertex, new HashSet<>());
}

private void dfs(Vertex<V, E> vertex, Set<Vertex<V, E>> set) {
System.out.println(vertex);
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
dfs(edge.to, set);
}
}
```

```    // Depth first search -- non recursive implementation
@Override
public void dfs(V begin) {
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;

Set<Vertex<V, E>> set = new HashSet<>();
Stack<Vertex<V, E>> stack = new Stack<>();
stack.push(beginVertex);
System.out.println(beginVertex);
while (!stack.isEmpty()) {
final Vertex<V, E> vertex = stack.pop();
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
stack.push(edge.from);
stack.push(edge.to);
System.out.println(edge.to);
break;
}
}
}
```

## Modify traversal interface

```public interface Graph<V, E> {
int vertexSize(); // Number of vertices
int edgeSize(); // Number of edges
void removeVertex(V v); // Delete a vertex
void addEdge(V from, V to); // Add an edge / / if a vertex does not exist, it will be created automatically
void addEdge(V from, V to, E weight); // Add an edge (with weight) / / if a vertex does not exist, it will be created automatically
void removeEdge(V from, V to); // Delete an edge

//    void bfs(V begin); //  Breadth first search traversal
//    void dfs(V begin); //  depth first search
void bfs(V begin, Visitor<V> visitor); // Breadth first search traversal
void dfs(V begin, Visitor<V> visitor); // depth first search
interface Visitor<V> {
void vertex(V v);
}
}
```
```    // Breadth first search
@Override
public void bfs(V begin, Visitor<V> visitor) {
if (null == visitor) return;
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;

Queue<Vertex<V, E>> queue = new LinkedList<>();
Set<Vertex<V, E>> set = new HashSet<>();
queue.offer(beginVertex);
while (!queue.isEmpty()) {
final Vertex<V, E> vertex = queue.poll();
visitor.vertex(vertex.value);
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
queue.offer(edge.to);
}
}
}

// Depth first search -- non recursive implementation
@Override
public void dfs(V begin, Visitor<V> visitor) {
if (null == visitor) return;
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;

Set<Vertex<V, E>> set = new HashSet<>();
Stack<Vertex<V, E>> stack = new Stack<>();
stack.push(beginVertex);
visitor.vertex(beginVertex.value);
while (!stack.isEmpty()) {
final Vertex<V, E> vertex = stack.pop();
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
stack.push(edge.from);
stack.push(edge.to);
visitor.vertex(edge.to.value);
break;
}
}

// Depth first search -- recursive implementation
public void dfs2(V begin, Visitor<V> visitor) {
if (null == visitor) return;
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;
dfs2(beginVertex, new HashSet<>(), visitor);
}

private void dfs2(Vertex<V, E> vertex, Set<Vertex<V, E>> set, Visitor<V> visitor) {
visitor.vertex(vertex.value);
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
dfs2(edge.to, set, visitor);
}
}
}
```

If you want to stop (exit) traversal during traversal:

```    void bfs(V begin, Visitor<V> visitor); // Breadth first search traversal
void dfs(V begin, Visitor<V> visitor); // depth first search
// You can stop traversal
interface Visitor<V> {
boolean vertex(V v); // If true is returned, the traversal is terminated
}
```
```// You can stop traversal

@Override
public void bfs(V begin, Visitor<V> visitor) {
if (null == visitor) return;
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;

Queue<Vertex<V, E>> queue = new LinkedList<>();
Set<Vertex<V, E>> set = new HashSet<>();
queue.offer(beginVertex);
while (!queue.isEmpty()) {
final Vertex<V, E> vertex = queue.poll();
if (visitor.vertex(vertex.value)) return;
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
queue.offer(edge.to);
}
}
}

// Depth first search -- non recursive implementation
@Override
public void dfs(V begin, Visitor<V> visitor) {
if (null == visitor) return;
final Vertex<V, E> beginVertex = vertices.get(begin);
if (null == beginVertex) return;

Set<Vertex<V, E>> set = new HashSet<>();
Stack<Vertex<V, E>> stack = new Stack<>();
stack.push(beginVertex);
if (visitor.vertex(beginVertex.value)) return;
while (!stack.isEmpty()) {
final Vertex<V, E> vertex = stack.pop();
for (Edge<V, E> edge : vertex.outEdges) {
if (set.contains(edge.to)) continue;
stack.push(edge.from);
stack.push(edge.to);
if (visitor.vertex(edge.to.value)) return;
break;
}
}
}
```

## AOV network

Homework: self study AOV network

## Topological sorting

The idea of Kahn algorithm is right, but when implementing the code, we must not delete the vertices, because this will destroy the original graph structure. Therefore, when using Kahn algorithm to realize topological sorting, we should make some modifications:

```    // The DAG is topologically sorted by Kahn algorithm
@Override
public List<V> topologicalSort() {
List<V> list = new ArrayList<>(); // list is used to store the traversal results and return them to the user
Queue<Vertex<V, E>> queue = new LinkedList<>(); // queue is used to store vertices with a penetration of 0
Map<Vertex<V, E>, Integer> map = new HashMap<>(); // Vertex entry table

// Initialize the degree table and queue (put the nodes with degree 0 into the queue)
vertices.forEach((v, vertex) -> {
int inSize = vertex.inEdges.size();
if (inSize == 0) queue.offer(vertex);
else
// When initializing the in meter, it is not necessary to put the vertices with the in degree of 0 into the in meter
map.put(vertex, inSize);
});

while (!queue.isEmpty()) {
Vertex<V, E> vertex = queue.poll();
vertex.outEdges.forEach(edge -> {
Integer integer = map.get(edge.to);
integer--;
if (integer == 0)
// After joining this vertex into the team, you don't need to update the penetration of this vertex
queue.offer(edge.to);
else map.put(edge.to, integer);
});
}

return list;
}
```

Test:

```public class Data {
public static final Object[][] TOPO = {
{0, 2},
{1, 0},
{2, 5}, {2, 6},
{3, 1}, {3, 5}, {3, 7},
{5, 7},
{6, 4},
{7, 6}
};
}
```
```    /**
* Directed graph
*/
private static Graph<Object, Double> directedGraph(Object[][] data) {
Graph<Object, Double> graph = new ListGraph<>();
for (Object[] edge : data) {
if (edge.length == 1) {
} else if (edge.length == 2) {
} else if (edge.length == 3) {
double weight = Double.parseDouble(edge[2].toString());
}
}
return graph;
}

/**
* Undirected graph
*/
private static Graph<Object, Double> undirectedGraph(Object[][] data) {
Graph<Object, Double> graph = new ListGraph<>();
for (Object[] edge : data) {
if (edge.length == 1) {
} else if (edge.length == 2) {
} else if (edge.length == 3) {
double weight = Double.parseDouble(edge[2].toString());
}
}
return graph;
}

private static void testTopologicalSort() {
Graph<Object, Double> graph = directedGraph(Data.TOPO);
graph.topologicalSort().forEach(System.out::println);
}

public static void main(String[] args) {
testTopologicalSort();
}
```

## reference resources

Little brother Li Mingjie's course: Fall in love with data structures and algorithms Season 2.