From 9e7fbf249fdad5b88592f24f239b7666c6b88391 Mon Sep 17 00:00:00 2001 From: khoaxuantu <68913255+khoaxuantu@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:30:55 +0700 Subject: [PATCH] feat: add ruby code - chapter graph (#1303) --- .../chapter_graph/graph_adjacency_list.rb | 116 ++++++++++++++++++ .../chapter_graph/graph_adjacency_matrix.rb | 116 ++++++++++++++++++ codes/ruby/chapter_graph/graph_bfs.rb | 61 +++++++++ codes/ruby/chapter_graph/graph_dfs.rb | 54 ++++++++ codes/ruby/utils/print_util.rb | 7 ++ codes/ruby/utils/vertex.rb | 24 ++++ 6 files changed, 378 insertions(+) create mode 100644 codes/ruby/chapter_graph/graph_adjacency_list.rb create mode 100644 codes/ruby/chapter_graph/graph_adjacency_matrix.rb create mode 100644 codes/ruby/chapter_graph/graph_bfs.rb create mode 100644 codes/ruby/chapter_graph/graph_dfs.rb create mode 100644 codes/ruby/utils/vertex.rb diff --git a/codes/ruby/chapter_graph/graph_adjacency_list.rb b/codes/ruby/chapter_graph/graph_adjacency_list.rb new file mode 100644 index 000000000..d17e5fa8e --- /dev/null +++ b/codes/ruby/chapter_graph/graph_adjacency_list.rb @@ -0,0 +1,116 @@ +=begin +File: graph_adjacency_list.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/vertex' + +### 基于邻接表实现的无向图类 ### +class GraphAdjList + attr_reader :adj_list + + ### 构造方法 ### + def initialize(edges) + # 邻接表,key:顶点,value:该顶点的所有邻接顶点 + @adj_list = {} + # 添加所有顶点和边 + for edge in edges + add_vertex(edge[0]) + add_vertex(edge[1]) + add_edge(edge[0], edge[1]) + end + end + + ### 获取顶点数量 ### + def size + @adj_list.length + end + + ### 添加边 ### + def add_edge(vet1, vet2) + raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) + + @adj_list[vet1] << vet2 + @adj_list[vet2] << vet1 + end + + ### 删除边 ### + def remove_edge(vet1, vet2) + raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) + + # 删除边 vet1 - vet2 + @adj_list[vet1].delete(vet2) + @adj_list[vet2].delete(vet1) + end + + ### 添加顶点 ### + def add_vertex(vet) + return if @adj_list.include?(vet) + + # 在邻接表中添加一个新链表 + @adj_list[vet] = [] + end + + ### 删除顶点 ### + def remove_vertex(vet) + raise ArgumentError unless @adj_list.include?(vet) + + # 在邻接表中删除顶点 vet 对应的链表 + @adj_list.delete(vet) + # 遍历其他顶点的链表,删除所有包含 vet 的边 + for vertex in @adj_list + @adj_list[vertex.first].delete(vet) if @adj_list[vertex.first].include?(vet) + end + end + + ### 打印邻接表 ### + def __print__ + puts '邻接表 =' + for vertex in @adj_list + tmp = @adj_list[vertex.first].map { |v| v.val } + puts "#{vertex.first.val}: #{tmp}," + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化无向图 + v = vals_to_vets([1, 3, 2, 5, 4]) + 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]], + ] + graph = GraphAdjList.new(edges) + puts "\n初始化后,图为" + graph.__print__ + + # 添加边 + # 顶点 1,2 即 v[0],v[2] + graph.add_edge(v[0], v[2]) + puts "\n添加边 1-2 后,图为" + graph.__print__ + + # 删除边 + # 顶点 1,3 即 v[0],v[1] + graph.remove_edge(v[0], v[1]) + puts "\n删除边 1-3 后,图为" + graph.__print__ + + # 添加顶点 + v5 = Vertex.new(6) + graph.add_vertex(v5) + puts "\n添加顶点 6 后,图为" + graph.__print__ + + # 删除顶点 + # 顶点 3 即 v[1] + graph.remove_vertex(v[1]) + puts "\n删除顶点 3 后,图为" + graph.__print__ +end diff --git a/codes/ruby/chapter_graph/graph_adjacency_matrix.rb b/codes/ruby/chapter_graph/graph_adjacency_matrix.rb new file mode 100644 index 000000000..ecfc21de8 --- /dev/null +++ b/codes/ruby/chapter_graph/graph_adjacency_matrix.rb @@ -0,0 +1,116 @@ +=begin +File: graph_adjacency_matrix.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/print_util' + +### 基于邻接矩阵实现的无向图类 ### +class GraphAdjMat + def initialize(vertices, edges) + ### 构造方法 ### + # 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + @vertices = [] + # 邻接矩阵,行列索引对应“顶点索引” + @adj_mat = [] + # 添加顶点 + vertices.each { |val| add_vertex(val) } + # 添加边 + # 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + edges.each { |e| add_edge(e[0], e[1]) } + end + + ### 获取顶点数量 ### + def size + @vertices.length + end + + ### 添加顶点 ### + def add_vertex(val) + n = size + # 向顶点列表中添加新顶点的值 + @vertices << val + # 在邻接矩阵中添加一行 + new_row = Array.new(n, 0) + @adj_mat << new_row + # 在邻接矩阵中添加一列 + @adj_mat.each { |row| row << 0 } + end + + ### 删除顶点 ### + def remove_vertex(index) + raise IndexError if index >= size + + # 在顶点列表中移除索引 index 的顶点 + @vertices.delete_at(index) + # 在邻接矩阵中删除索引 index 的行 + @adj_mat.delete_at(index) + # 在邻接矩阵中删除索引 index 的列 + @adj_mat.each { |row| row.delete_at(index) } + end + + ### 添加边 ### + def add_edge(i, j) + # 参数 i, j 对应 vertices 元素索引 + # 索引越界与相等处理 + if i < 0 || j < 0 || i >= size || j >= size || i == j + raise IndexError + end + # 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) + @adj_mat[i][j] = 1 + @adj_mat[j][i] = 1 + end + + ### 删除边 ### + def remove_edge(i, j) + # 参数 i, j 对应 vertices 元素索引 + # 索引越界与相等处理 + if i < 0 || j < 0 || i >= size || j >= size || i == j + raise IndexError + end + @adj_mat[i][j] = 0 + @adj_mat[j][i] = 0 + end + + ### 打印邻接矩阵 ### + def __print__ + puts "顶点列表 = #{@vertices}" + puts '邻接矩阵 =' + print_matrix(@adj_mat) + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化无向图 + # 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + vertices = [1, 3, 2, 5, 4] + edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] + graph = GraphAdjMat.new(vertices, edges) + puts "\n初始化后,图为" + graph.__print__ + + # 添加边 + # 顶点 1, 2 的索引分别为 0, 2 + graph.add_edge(0, 2) + puts "\n添加边 1-2 后,图为" + graph.__print__ + + # 删除边 + # 定点 1, 3 的索引分别为 0, 1 + graph.remove_edge(0, 1) + puts "\n删除边 1-3 后,图为" + graph.__print__ + + # 添加顶点 + graph.add_vertex(6) + puts "\n添加顶点 6 后,图为" + graph.__print__ + + # 删除顶点 + # 顶点 3 的索引为 1 + graph.remove_vertex(1) + puts "\n删除顶点 3 后,图为" + graph.__print__ +end diff --git a/codes/ruby/chapter_graph/graph_bfs.rb b/codes/ruby/chapter_graph/graph_bfs.rb new file mode 100644 index 000000000..525b92923 --- /dev/null +++ b/codes/ruby/chapter_graph/graph_bfs.rb @@ -0,0 +1,61 @@ +=begin +File: graph_bfs.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require 'set' +require_relative './graph_adjacency_list' +require_relative '../utils/vertex' + +### 广度优先遍历 ### +def graph_bfs(graph, start_vet) + # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + # 顶点遍历序列 + res = [] + # 哈希表,用于记录已被访问过的顶点 + visited = Set.new([start_vet]) + # 队列用于实现 BFS + que = [start_vet] + # 以顶点 vet 为起点,循环直至访问完所有顶点 + while que.length > 0 + vet = que.shift # 队首顶点出队 + res << vet # 记录访问顶点 + # 遍历该顶点的所有邻接顶点 + for adj_vet in graph.adj_list[vet] + next if visited.include?(adj_vet) # 跳过已被访问的顶点 + que << adj_vet # 只入队未访问的顶点 + visited.add(adj_vet) # 标记该顶点已被访问 + end + end + # 返回顶点遍历序列 + res +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化无向图 + v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + 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]], + ] + graph = GraphAdjList.new(edges) + puts "\n初始化后,图为" + graph.__print__ + + # 广度优先遍历 + res = graph_bfs(graph, v.first) + puts "\n广度优先便利(BFS)顶点序列为" + p vets_to_vals(res) +end diff --git a/codes/ruby/chapter_graph/graph_dfs.rb b/codes/ruby/chapter_graph/graph_dfs.rb new file mode 100644 index 000000000..c4633850d --- /dev/null +++ b/codes/ruby/chapter_graph/graph_dfs.rb @@ -0,0 +1,54 @@ +=begin +File: graph_dfs.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require 'set' +require_relative './graph_adjacency_list' +require_relative '../utils/vertex' + +### 深度优先遍历辅助函数 ### +def dfs(graph, visited, res, vet) + res << vet # 记录访问顶点 + visited.add(vet) # 标记该顶点已被访问 + # 遍历该顶点的所有邻接顶点 + for adj_vet in graph.adj_list[vet] + next if visited.include?(adj_vet) # 跳过已被访问的顶点 + # 递归访问邻接顶点 + dfs(graph, visited, res, adj_vet) + end +end + +### 深度优先遍历 ### +def graph_dfs(graph, start_vet) + # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + # 顶点遍历序列 + res = [] + # 哈希表,用于记录已被访问过的顶点 + visited = Set.new + dfs(graph, visited, res, start_vet) + res +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化无向图 + v = vals_to_vets([0, 1, 2, 3, 4, 5, 6]) + 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]], + ] + graph = GraphAdjList.new(edges) + puts "\n初始化后,图为" + graph.__print__ + + # 深度优先遍历 + res = graph_dfs(graph, v[0]) + puts "\n深度优先遍历(DFS)顶点序列为" + p vets_to_vals(res) +end diff --git a/codes/ruby/utils/print_util.rb b/codes/ruby/utils/print_util.rb index 52c74a0f8..7f241c405 100644 --- a/codes/ruby/utils/print_util.rb +++ b/codes/ruby/utils/print_util.rb @@ -4,6 +4,13 @@ Created Time: 2024-03-18 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end +### 打印矩阵 ### +def print_matrix(mat) + s = [] + mat.each { |arr| s << " #{arr.to_s}" } + puts "[\n#{s.join(",\n")}\n]" +end + ### 打印链表 ### def print_linked_list(head) list = [] diff --git a/codes/ruby/utils/vertex.rb b/codes/ruby/utils/vertex.rb new file mode 100644 index 000000000..bf40e1ea5 --- /dev/null +++ b/codes/ruby/utils/vertex.rb @@ -0,0 +1,24 @@ +=begin +File: vertex.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 顶点类 ### +class Vertex + attr_accessor :val + + def initialize(val) + @val = val + end +end + +### 输入值列表 vals ,返回顶点列表 vets ### +def vals_to_vets(vals) + Array.new(vals.length) { |i| Vertex.new(vals[i]) } +end + +### 输入顶点列表 vets, 返回值列表 vals ### +def vets_to_vals(vets) + Array.new(vets.length) { |i| vets[i].val } +end