Skip to content

Commit

Permalink
Add the section of Graph Traversal.
Browse files Browse the repository at this point in the history
  • Loading branch information
krahets committed Feb 14, 2023
1 parent c74f829 commit dc44192
Show file tree
Hide file tree
Showing 31 changed files with 383 additions and 54 deletions.
48 changes: 19 additions & 29 deletions codes/java/chapter_graph/graph_adjacency_list.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@
package chapter_graph;

import java.util.*;

/* 顶点类 */
class Vertex {
int val;
public Vertex(int val) {
this.val = val;
}
}
import include.*;

/* 基于邻接表实现的无向图类 */
class GraphAdjList {
// 请注意,vertices 和 adjList 中存储的都是 Vertex 对象
Map<Vertex, Set<Vertex>> adjList; // 邻接表(使用哈希表实现)
// 邻接表,使用哈希表来代替链表,以提升删除边、删除顶点的效率
// 请注意,adjList 中的元素是 Vertex 对象
Map<Vertex, List<Vertex>> adjList;

/* 构造方法 */
public GraphAdjList(Vertex[][] edges) {
Expand Down Expand Up @@ -59,26 +53,26 @@ public void removeEdge(Vertex vet1, Vertex vet2) {
public void addVertex(Vertex vet) {
if (adjList.containsKey(vet))
return;
// 在邻接表中添加一个新链表(即 HashSet)
adjList.put(vet, new HashSet<>());
// 在邻接表中添加一个新链表
adjList.put(vet, new ArrayList<>());
}

/* 删除顶点 */
public void removeVertex(Vertex vet) {
if (!adjList.containsKey(vet))
throw new IllegalArgumentException();
// 在邻接表中删除顶点 vet 对应的链表(即 HashSet)
// 在邻接表中删除顶点 vet 对应的链表
adjList.remove(vet);
// 遍历其它顶点的链表(即 HashSet),删除所有包含 vet 的边
for (Set<Vertex> set : adjList.values()) {
set.remove(vet);
// 遍历其它顶点的链表,删除所有包含 vet 的边
for (List<Vertex> list : adjList.values()) {
list.remove(vet);
}
}

/* 打印邻接表 */
public void print() {
System.out.println("邻接表 =");
for (Map.Entry<Vertex, Set<Vertex>> entry : adjList.entrySet()) {
for (Map.Entry<Vertex, List<Vertex>> entry : adjList.entrySet()) {
List<Integer> tmp = new ArrayList<>();
for (Vertex vertex : entry.getValue())
tmp.add(vertex.val);
Expand All @@ -90,25 +84,21 @@ public void print() {
public class graph_adjacency_list {
public static void main(String[] args) {
/* 初始化无向图 */
Vertex v0 = new Vertex(1),
v1 = new Vertex(3),
v2 = new Vertex(2),
v3 = new Vertex(5),
v4 = new Vertex(4);
Vertex[][] edges = { { v0, v1 }, { v1, v2 }, { v2, v3 }, { v0, v3 }, { v2, v4 }, { v3, v4 } };
Vertex[] v = Vertex.valsToVets(new int[] { 1, 3, 2, 5, 4 });
Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[2], v[3] }, { v[2], v[4] }, { v[3], v[4] } };
GraphAdjList graph = new GraphAdjList(edges);
System.out.println("\n初始化后,图为");
graph.print();

/* 添加边 */
// 顶点 1, 2 即 v0, v2
graph.addEdge(v0, v2);
// 顶点 1, 2 即 v[0], v[2]
graph.addEdge(v[0], v[2]);
System.out.println("\n添加边 1-2 后,图为");
graph.print();

/* 删除边 */
// 顶点 1, 3 即 v0, v1
graph.removeEdge(v0, v1);
// 顶点 1, 3 即 v[0], v[1]
graph.removeEdge(v[0], v[1]);
System.out.println("\n删除边 1-3 后,图为");
graph.print();

Expand All @@ -119,8 +109,8 @@ public static void main(String[] args) {
graph.print();

/* 删除顶点 */
// 顶点 3 即 v1
graph.removeVertex(v1);
// 顶点 3 即 v[1]
graph.removeVertex(v[1]);
System.out.println("\n删除顶点 3 后,图为");
graph.print();
}
Expand Down
2 changes: 1 addition & 1 deletion codes/java/chapter_graph/graph_adjacency_matrix.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public static void main(String[] args) {
/* 初始化无向图 */
// 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引
int[] vertices = { 1, 3, 2, 5, 4 };
int[][] edges = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 0, 3 }, { 2, 4 }, { 3, 4 } };
int[][] edges = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 2, 3 }, { 2, 4 }, { 3, 4 } };
GraphAdjMat graph = new GraphAdjMat(vertices, edges);
System.out.println("\n初始化后,图为");
graph.print();
Expand Down
53 changes: 53 additions & 0 deletions codes/java/chapter_graph/graph_bfs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* File: graph_bfs.java
* Created Time: 2023-02-12
* Author: Krahets ([email protected])
*/

package chapter_graph;

import java.util.*;
import include.*;

public class graph_bfs {
/* 广度优先遍历 BFS */
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
static List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
// 顶点遍历序列
List<Vertex> res = new ArrayList<>();
// 哈希表,用于记录已被访问过的顶点
Set<Vertex> visited = new HashSet<>() {{ add(startVet); }};
// 队列用于实现 BFS
Queue<Vertex> que = new LinkedList<>() {{ offer(startVet); }};
// 以顶点 vet 为起点,循环直至访问完所有顶点
while (!que.isEmpty()) {
Vertex vet = que.poll(); // 队首顶点出队
res.add(vet); // 记录访问顶点
// 遍历该顶点的所有邻接顶点
for (Vertex adjVet : graph.adjList.get(vet)) {
if (visited.contains(adjVet))
continue; // 跳过已被访问过的顶点
que.offer(adjVet); // 只入队未访问的顶点
visited.add(adjVet); // 标记该顶点已被访问
}
}
// 返回顶点遍历序列
return res;
}

public static void main(String[] args) {
/* 初始化无向图 */
Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[1], v[4] },
{ v[2], v[5] }, { v[3], v[4] }, { v[3], v[6] }, { v[4], v[5] },
{ v[4], v[7] }, { v[5], v[8] }, { v[6], v[7] }, { v[7], v[8] } };
GraphAdjList graph = new GraphAdjList(edges);
System.out.println("\n初始化后,图为");
graph.print();

/* 广度优先遍历 BFS */
List<Vertex> res = graphBFS(graph, v[0]);
System.out.println("\n广度优先遍历(BFS)顶点序列为");
System.out.println(Vertex.vetsToVals(res));
}
}
51 changes: 51 additions & 0 deletions codes/java/chapter_graph/graph_dfs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* File: graph_dfs.java
* Created Time: 2023-02-12
* Author: Krahets ([email protected])
*/

package chapter_graph;

import java.util.*;
import include.*;

public class graph_dfs {
/* 深度优先遍历 DFS 辅助函数 */
static void dfs(GraphAdjList graph, Set<Vertex> visited, List<Vertex> res, Vertex vet) {
res.add(vet); // 记录访问顶点
visited.add(vet); // 标记该顶点已被访问
// 遍历该顶点的所有邻接顶点
for (Vertex adjVet : graph.adjList.get(vet)) {
if (visited.contains(adjVet))
continue; // 跳过已被访问过的顶点
// 递归访问邻接顶点
dfs(graph, visited, res, adjVet);
}
}

/* 深度优先遍历 DFS */
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
static List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
// 顶点遍历序列
List<Vertex> res = new ArrayList<>();
// 哈希表,用于记录已被访问过的顶点
Set<Vertex> visited = new HashSet<>();
dfs(graph, visited, res, startVet);
return res;
}

public static void main(String[] args) {
/* 初始化无向图 */
Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6 });
Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] },
{ v[2], v[5] }, { v[4], v[5] }, { v[5], v[6] } };
GraphAdjList graph = new GraphAdjList(edges);
System.out.println("\n初始化后,图为");
graph.print();

/* 深度优先遍历 BFS */
List<Vertex> res = graphDFS(graph, v[0]);
System.out.println("\n深度优先遍历(DFS)顶点序列为");
System.out.println(Vertex.vetsToVals(res));
}
}
8 changes: 5 additions & 3 deletions docs/chapter_graph/graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ $$

根据边是否有方向,分为「无向图 Undirected Graph」和「有向图 Directed Graph」。

- 在无向图中,边表示两结点之间“双向”的连接关系,例如微信或 QQ 中的“好友关系”;
- 在无向图中,边表示两顶点之间“双向”的连接关系,例如微信或 QQ 中的“好友关系”;
- 在有向图中,边是有方向的,即 $A \rightarrow B$ 和 $A \leftarrow B$ 两个方向的边是相互独立的,例如微博或抖音上的“关注”与“被关注”关系;

![directed_graph](graph.assets/directed_graph.png)

根据所有顶点是否连通,分为「连通图 Connected Graph」和「非连通图 Disconnected Graph」。

- 对于连通图,从某个结点出发,可以到达其余任意结点
- 对于非连通图,从某个结点出发,至少有一个结点无法到达
- 对于连通图,从某个顶点出发,可以到达其余任意顶点
- 对于非连通图,从某个顶点出发,至少有一个顶点无法到达

![connected_graph](graph.assets/connected_graph.png)

Expand All @@ -52,6 +52,8 @@ $$

设图的顶点数量为 $n$ ,「邻接矩阵 Adjacency Matrix」使用一个 $n \times n$ 大小的矩阵来表示图,每一行(列)代表一个顶点,矩阵元素代表边,使用 $1$ 或 $0$ 来表示两个顶点之间有边或无边。

如下图所示,记邻接矩阵为 $M$ 、顶点列表为 $V$ ,则矩阵元素 $M[i][j] = 1$ 代表着顶点 $V[i]$ 到顶点 $V[j]$ 之间有边,相反地 $M[i][j] = 0$ 代表两顶点之间无边。

![adjacency_matrix](graph.assets/adjacency_matrix.png)

邻接矩阵具有以下性质:
Expand Down
28 changes: 17 additions & 11 deletions docs/chapter_graph/graph_operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ comments: true
=== "Zig"

```zig title="graph_adjacency_matrix.zig"

```

## 9.2.2. 基于邻接表的实现
Expand Down Expand Up @@ -119,83 +119,89 @@ comments: true

基于邻接表实现图的代码如下所示。

!!! question "为什么需要使用顶点类 `Vertex` ?"

如果我们直接通过顶点值来区分不同顶点,那么值重复的顶点将无法被区分。
如果建立一个顶点列表,用索引来区分不同顶点,那么假设我们想要删除索引为 `i` 的顶点,则需要遍历整个邻接表,将其中 $> i$ 的索引全部执行 $-1$ ,这样的操作是比较耗时的。
因此,通过引入顶点类 `Vertex` ,每个顶点都是唯一的对象,这样在删除操作时就无需改动其余顶点了。

=== "Java"

```java title="graph_adjacency_list.java"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "C++"

```cpp title="graph_adjacency_list.cpp"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "Python"

```python title="graph_adjacency_list.py"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "Go"

```go title="graph_adjacency_list.go"
[class]{vertex}-[func]{}

[class]{graphAdjList}-[func]{}
```

=== "JavaScript"

```javascript title="graph_adjacency_list.js"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "TypeScript"

```typescript title="graph_adjacency_list.ts"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "C"

```c title="graph_adjacency_list.c"
[class]{vertex}-[func]{}

[class]{graphAdjList}-[func]{}
```

=== "C#"

```csharp title="graph_adjacency_list.cs"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "Swift"

```swift title="graph_adjacency_list.swift"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

=== "Zig"

```zig title="graph_adjacency_list.zig"
[class]{Vertex}-[func]{}

[class]{GraphAdjList}-[func]{}
```

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit dc44192

Please sign in to comment.