diff --git a/codes/c/.gitignore b/codes/c/.gitignore index 08b67f32c..3331318e1 100644 --- a/codes/c/.gitignore +++ b/codes/c/.gitignore @@ -4,5 +4,4 @@ !*.* # Unignore all dirs !*/ - *.dSYM/ \ No newline at end of file diff --git a/codes/go/chapter_graph/graph_adjacency_list.go b/codes/go/chapter_graph/graph_adjacency_list.go new file mode 100644 index 000000000..762ad1140 --- /dev/null +++ b/codes/go/chapter_graph/graph_adjacency_list.go @@ -0,0 +1,111 @@ +// File: graph_adjacency_list.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "strconv" + "strings" +) + +/* 顶点类 */ +type vertex struct { + val int +} + +func newVertex(val int) vertex { + return vertex{ + val: val, + } +} + +/* 基于邻接表实现的无向图类 */ +type graphAdjList struct { + // 请注意,vertices 和 adjList 中存储的都是 Vertex 对象 + // 邻接表(使用哈希表实现), 使用哈希表模拟集合 + adjList map[vertex]map[vertex]struct{} +} + +/* 构造函数 */ +func newGraphAdjList(edges [][]vertex) *graphAdjList { + g := &graphAdjList{ + adjList: make(map[vertex]map[vertex]struct{}), + } + // 添加所有顶点和边 + for _, edge := range edges { + g.addVertex(edge[0]) + g.addVertex(edge[1]) + g.addEdge(edge[0], edge[1]) + } + return g +} + +/* 获取顶点数量 */ +func (g *graphAdjList) size() int { + return len(g.adjList) +} + +/* 添加边 */ +func (g *graphAdjList) addEdge(vet1 vertex, vet2 vertex) { + _, ok1 := g.adjList[vet1] + _, ok2 := g.adjList[vet2] + if !ok1 || !ok2 || vet1 == vet2 { + panic("error") + } + // 添加边 vet1 - vet2, 添加匿名 struct{}, + g.adjList[vet1][vet2] = struct{}{} + g.adjList[vet2][vet1] = struct{}{} +} + +/* 删除边 */ +func (g *graphAdjList) removeEdge(vet1 vertex, vet2 vertex) { + _, ok1 := g.adjList[vet1] + _, ok2 := g.adjList[vet2] + if !ok1 || !ok2 || vet1 == vet2 { + panic("error") + } + // 删除边 vet1 - vet2, 借助 delete 来删除 map 中的键 + delete(g.adjList[vet1], vet2) + delete(g.adjList[vet2], vet1) +} + +/* 添加顶点 */ +func (g *graphAdjList) addVertex(vet vertex) { + _, ok := g.adjList[vet] + if ok { + return + } + // 在邻接表中添加一个新链表(即 set) + g.adjList[vet] = make(map[vertex]struct{}) +} + +/* 删除顶点 */ +func (g *graphAdjList) removeVertex(vet vertex) { + _, ok := g.adjList[vet] + if !ok { + panic("error") + } + // 在邻接表中删除顶点 vet 对应的链表 + delete(g.adjList, vet) + // 遍历其它顶点的链表(即 Set),删除所有包含 vet 的边 + for _, set := range g.adjList { + // 操作 + delete(set, vet) + } +} + +/* 打印邻接表 */ +func (g *graphAdjList) print() { + var builder strings.Builder + fmt.Printf("邻接表 = \n") + for k, v := range g.adjList { + builder.WriteString("\t\t" + strconv.Itoa(k.val) + ": ") + for vet := range v { + builder.WriteString(strconv.Itoa(vet.val) + " ") + } + fmt.Println(builder.String()) + builder.Reset() + } +} diff --git a/codes/go/chapter_graph/graph_adjacency_list_test.go b/codes/go/chapter_graph/graph_adjacency_list_test.go new file mode 100644 index 000000000..630bc494f --- /dev/null +++ b/codes/go/chapter_graph/graph_adjacency_list_test.go @@ -0,0 +1,47 @@ +// File: graph_adjacency_list_test.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "testing" +) + +func TestGraphAdjList(t *testing.T) { + /* 初始化无向图 */ + v0 := newVertex(1) + v1 := newVertex(3) + v2 := newVertex(2) + v3 := newVertex(5) + v4 := newVertex(4) + edges := [][]vertex{{v0, v1}, {v1, v2}, {v2, v3}, {v0, v3}, {v2, v4}, {v3, v4}} + graph := newGraphAdjList(edges) + fmt.Println("初始化后,图为:") + graph.print() + + /* 添加边 */ + // 顶点 1, 2 即 v0, v2 + graph.addEdge(v0, v2) + fmt.Println("\n添加边 1-2 后,图为") + graph.print() + + /* 删除边 */ + // 顶点 1, 3 即 v0, v1 + graph.removeEdge(v0, v1) + fmt.Println("\n删除边 1-3 后,图为") + graph.print() + + /* 添加顶点 */ + v5 := newVertex(6) + graph.addVertex(v5) + fmt.Println("\n添加顶点 6 后,图为") + graph.print() + + /* 删除顶点 */ + // 顶点 3 即 v1 + graph.removeVertex(v1) + fmt.Println("\n删除顶点 3 后,图为") + graph.print() +} diff --git a/codes/go/chapter_graph/graph_adjacency_matrix.go b/codes/go/chapter_graph/graph_adjacency_matrix.go new file mode 100644 index 000000000..a182b469f --- /dev/null +++ b/codes/go/chapter_graph/graph_adjacency_matrix.go @@ -0,0 +1,101 @@ +// File: graph_adjacency_matrix.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import "fmt" + +/* 基于邻接矩阵实现的无向图类 */ +type graphAdjMat struct { + // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + vertices []int + // 邻接矩阵,行列索引对应“顶点索引” + adjMat [][]int +} + +func newGraphAdjMat(vertices []int, edges [][]int) *graphAdjMat { + // 添加顶点 + n := len(vertices) + adjMat := make([][]int, n) + for i := range adjMat { + adjMat[i] = make([]int, n) + } + // 初始化图 + g := &graphAdjMat{ + vertices: vertices, + adjMat: adjMat, + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for i := range edges { + g.addEdge(edges[i][0], edges[i][1]) + } + return g +} + +/* 获取顶点数量 */ +func (g *graphAdjMat) size() int { + return len(g.vertices) +} + +/* 添加顶点 */ +func (g *graphAdjMat) addVertex(val int) { + n := g.size() + // 向顶点列表中添加新顶点的值 + g.vertices = append(g.vertices, val) + // 在邻接矩阵中添加一行 + newRow := make([]int, n) + g.adjMat = append(g.adjMat, newRow) + // 在邻接矩阵中添加一列 + for i := range g.adjMat { + g.adjMat[i] = append(g.adjMat[i], 0) + } +} + +/* 删除顶点 */ +func (g *graphAdjMat) removeVertex(index int) { + if index >= g.size() { + return + } + // 在顶点列表中移除索引 index 的顶点 + g.vertices = append(g.vertices[:index], g.vertices[index+1:]...) + // 在邻接矩阵中删除索引 index 的行 + g.adjMat = append(g.adjMat[:index], g.adjMat[index+1:]...) + // 在邻接矩阵中删除索引 index 的列 + for i := range g.adjMat { + g.adjMat[i] = append(g.adjMat[i][:index], g.adjMat[i][index+1:]...) + } +} + +/* 添加边 */ +// 参数 i, j 对应 vertices 元素索引 +func (g *graphAdjMat) addEdge(i, j int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { + fmt.Errorf("%s", "Index Out Of Bounds Exception") + } + // 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i) + g.adjMat[i][j] = 1 + g.adjMat[j][i] = 1 +} + +/* 删除边 */ +// 参数 i, j 对应 vertices 元素索引 +func (g *graphAdjMat) removeEdge(i, j int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { + fmt.Errorf("%s", "Index Out Of Bounds Exception") + } + g.adjMat[i][j] = 0 + g.adjMat[j][i] = 0 +} + +/* 打印邻接矩阵 */ +func (g *graphAdjMat) print() { + fmt.Printf("\t顶点列表 = %v\n", g.vertices) + fmt.Printf("\t邻接矩阵 = \n") + for i := range g.adjMat { + fmt.Printf("\t\t\t%v\n", g.adjMat[i]) + } +} diff --git a/codes/go/chapter_graph/graph_adjacency_matrix_test.go b/codes/go/chapter_graph/graph_adjacency_matrix_test.go new file mode 100644 index 000000000..1a4755791 --- /dev/null +++ b/codes/go/chapter_graph/graph_adjacency_matrix_test.go @@ -0,0 +1,43 @@ +// File: graph_adjacency_matrix_test.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "testing" +) + +func TestGraphAdjMat(t *testing.T) { + /* 初始化无向图 */ + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + vertices := []int{1, 3, 2, 5, 4} + edges := [][]int{{0, 1}, {0, 2}, {1, 2}, {2, 3}, {0, 3}, {2, 4}, {3, 4}} + graph := newGraphAdjMat(vertices, edges) + fmt.Println("初始化后,图为:") + graph.print() + + /* 添加边 */ + // 顶点 1, 2 的索引分别为 0, 2 + graph.addEdge(0, 2) + fmt.Println("添加边 1-2 后,图为") + graph.print() + + /* 删除边 */ + // 顶点 1, 3 的索引分别为 0, 1 + graph.removeEdge(0, 1) + fmt.Println("删除边 1-3 后,图为") + graph.print() + + /* 添加顶点 */ + graph.addVertex(6) + fmt.Println("添加顶点 6 后,图为") + graph.print() + + /* 删除顶点 */ + // 顶点 3 的索引为 1 + graph.removeVertex(1) + fmt.Println("删除顶点 3 后,图为") + graph.print() +} diff --git a/docs/chapter_graph/graph_operations.md b/docs/chapter_graph/graph_operations.md index 3ce7f122d..4b86d4d39 100644 --- a/docs/chapter_graph/graph_operations.md +++ b/docs/chapter_graph/graph_operations.md @@ -129,7 +129,90 @@ comments: true === "Go" ```go title="graph_adjacency_matrix.go" + /* 基于邻接矩阵实现的无向图类 */ + type graphAdjMat struct { + // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + vertices []int + // 邻接矩阵,行列索引对应“顶点索引” + adjMat [][]int + } + + func newGraphAdjMat(vertices []int, edges [][]int) *graphAdjMat { + // 添加顶点 + n := len(vertices) + adjMat := make([][]int, n) + for i := range adjMat { + adjMat[i] = make([]int, n) + } + // 初始化图 + g := &graphAdjMat{ + vertices: vertices, + adjMat: adjMat, + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for i := range edges { + g.addEdge(edges[i][0], edges[i][1]) + } + return g + } + + /* 获取顶点数量 */ + func (g *graphAdjMat) size() int { + return len(g.vertices) + } + + /* 添加顶点 */ + func (g *graphAdjMat) addVertex(val int) { + n := g.size() + // 向顶点列表中添加新顶点的值 + g.vertices = append(g.vertices, val) + // 在邻接矩阵中添加一行 + newRow := make([]int, n) + g.adjMat = append(g.adjMat, newRow) + // 在邻接矩阵中添加一列 + for i := range g.adjMat { + g.adjMat[i] = append(g.adjMat[i], 0) + } + } + /* 删除顶点 */ + func (g *graphAdjMat) removeVertex(index int) { + if index >= g.size() { + return + } + // 在顶点列表中移除索引 index 的顶点 + g.vertices = append(g.vertices[:index], g.vertices[index+1:]...) + // 在邻接矩阵中删除索引 index 的行 + g.adjMat = append(g.adjMat[:index], g.adjMat[index+1:]...) + // 在邻接矩阵中删除索引 index 的列 + for i := range g.adjMat { + g.adjMat[i] = append(g.adjMat[i][:index], g.adjMat[i][index+1:]...) + } + } + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + func (g *graphAdjMat) addEdge(i, j int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { + fmt.Errorf("%s", "Index Out Of Bounds Exception") + } + // 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i) + g.adjMat[i][j] = 1 + g.adjMat[j][i] = 1 + } + + /* 删除边 */ + // 参数 i, j 对应 vertices 元素索引 + func (g *graphAdjMat) removeEdge(i, j int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { + fmt.Errorf("%s", "Index Out Of Bounds Exception") + } + g.adjMat[i][j] = 0 + g.adjMat[j][i] = 0 + } ``` === "JavaScript" @@ -358,7 +441,91 @@ comments: true === "Go" ```go title="graph_adjacency_list.go" + /* 顶点类 */ + type vertex struct { + val int + } + + func newVertex(val int) vertex { + return vertex{ + val: val, + } + } + + /* 基于邻接表实现的无向图类 */ + type graphAdjList struct { + // 请注意,vertices 和 adjList 中存储的都是 Vertex 对象 + // 邻接表(使用哈希表实现), 使用哈希表模拟集合 + adjList map[vertex]map[vertex]struct{} + } + + /* 构造函数 */ + func newGraphAdjList(edges [][]vertex) *graphAdjList { + g := &graphAdjList{ + adjList: make(map[vertex]map[vertex]struct{}), + } + // 添加所有顶点和边 + for _, edge := range edges { + g.addVertex(edge[0]) + g.addVertex(edge[1]) + g.addEdge(edge[0], edge[1]) + } + return g + } + + /* 获取顶点数量 */ + func (g *graphAdjList) size() int { + return len(g.adjList) + } + /* 添加边 */ + func (g *graphAdjList) addEdge(vet1 vertex, vet2 vertex) { + _, ok1 := g.adjList[vet1] + _, ok2 := g.adjList[vet2] + if !ok1 || !ok2 || vet1 == vet2 { + panic("error") + } + // 添加边 vet1 - vet2, 添加匿名 struct{}, + g.adjList[vet1][vet2] = struct{}{} + g.adjList[vet2][vet1] = struct{}{} + } + + /* 删除边 */ + func (g *graphAdjList) removeEdge(vet1 vertex, vet2 vertex) { + _, ok1 := g.adjList[vet1] + _, ok2 := g.adjList[vet2] + if !ok1 || !ok2 || vet1 == vet2 { + panic("error") + } + // 删除边 vet1 - vet2, 借助 delete 来删除 map 中的键 + delete(g.adjList[vet1], vet2) + delete(g.adjList[vet2], vet1) + } + + /* 添加顶点 */ + func (g *graphAdjList) addVertex(vet vertex) { + _, ok := g.adjList[vet] + if ok { + return + } + // 在邻接表中添加一个新链表(即 set) + g.adjList[vet] = make(map[vertex]struct{}) + } + + /* 删除顶点 */ + func (g *graphAdjList) removeVertex(vet vertex) { + _, ok := g.adjList[vet] + if !ok { + panic("error") + } + // 在邻接表中删除顶点 vet 对应的链表 + delete(g.adjList, vet) + // 遍历其它顶点的链表(即 Set),删除所有包含 vet 的边 + for _, set := range g.adjList { + // 操作 + delete(set, vet) + } + } ``` === "JavaScript"