diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index 28f51cc5f..417749e16 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -28,6 +28,8 @@ let package = Package( .executable(name: "binary_search_tree", targets: ["binary_search_tree"]), .executable(name: "avl_tree", targets: ["avl_tree"]), .executable(name: "my_heap", targets: ["my_heap"]), + .executable(name: "graph_adjacency_matrix", targets: ["graph_adjacency_matrix"]), + .executable(name: "graph_adjacency_list", targets: ["graph_adjacency_list"]), .executable(name: "linear_search", targets: ["linear_search"]), .executable(name: "binary_search", targets: ["binary_search"]), .executable(name: "hashing_search", targets: ["hashing_search"]), @@ -62,6 +64,8 @@ let package = Package( .executableTarget(name: "binary_search_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_search_tree.swift"]), .executableTarget(name: "avl_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["avl_tree.swift"]), .executableTarget(name: "my_heap", dependencies: ["utils"], path: "chapter_heap", sources: ["my_heap.swift"]), + .executableTarget(name: "graph_adjacency_matrix", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_matrix.swift"]), + .executableTarget(name: "graph_adjacency_list", path: "chapter_graph", sources: ["graph_adjacency_list.swift"]), .executableTarget(name: "linear_search", dependencies: ["utils"], path: "chapter_searching", sources: ["linear_search.swift"]), .executableTarget(name: "binary_search", path: "chapter_searching", sources: ["binary_search.swift"]), .executableTarget(name: "hashing_search", dependencies: ["utils"], path: "chapter_searching", sources: ["hashing_search.swift"]), diff --git a/codes/swift/chapter_graph/graph_adjacency_list.swift b/codes/swift/chapter_graph/graph_adjacency_list.swift new file mode 100644 index 000000000..7185c542d --- /dev/null +++ b/codes/swift/chapter_graph/graph_adjacency_list.swift @@ -0,0 +1,138 @@ +/** + * File: graph_adjacency_list.swift + * Created Time: 2023-02-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* 顶点类 */ +class Vertex: Hashable { + var val: Int + + init(val: Int) { + self.val = val + } + + static func == (lhs: Vertex, rhs: Vertex) -> Bool { + lhs.val == rhs.val + } + + func hash(into hasher: inout Hasher) { + hasher.combine(val) + } +} + +/* 基于邻接表实现的无向图类 */ +class GraphAdjList { + // 请注意,vertices 和 adjList 中存储的都是 Vertex 对象 + private var adjList: [Vertex: Set] // 邻接表(使用哈希表实现) + + init(edges: [[Vertex]]) { + adjList = [:] + // 添加所有顶点和边 + for edge in edges { + addVertex(vet: edge[0]) + addVertex(vet: edge[1]) + addEdge(vet1: edge[0], vet2: edge[1]) + } + } + + /* 获取顶点数量 */ + func size() -> Int { + adjList.count + } + + /* 添加边 */ + func addEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("参数错误") + } + // 添加边 vet1 - vet2 + adjList[vet1]?.insert(vet2) + adjList[vet2]?.insert(vet1) + } + + /* 删除边 */ + func removeEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("参数错误") + } + // 删除边 vet1 - vet2 + adjList[vet1]?.remove(vet2) + adjList[vet2]?.remove(vet1) + } + + /* 添加顶点 */ + func addVertex(vet: Vertex) { + if adjList[vet] != nil { + return + } + // 在邻接表中添加一个新链表(即 HashSet) + adjList[vet] = [] + } + + /* 删除顶点 */ + func removeVertex(vet: Vertex) { + if adjList[vet] == nil { + fatalError("参数错误") + } + // 在邻接表中删除顶点 vet 对应的链表(即 HashSet) + adjList.removeValue(forKey: vet) + // 遍历其它顶点的链表(即 HashSet),删除所有包含 vet 的边 + for key in adjList.keys { + adjList[key]?.remove(vet) + } + } + + /* 打印邻接表 */ + func print() { + Swift.print("邻接表 =") + for entry in adjList { + var tmp: [Int] = [] + for vertex in entry.value { + tmp.append(vertex.val) + } + Swift.print("\(entry.key.val): \(tmp),") + } + } +} + +@main +enum GraphAdjacencyList { + /* Driver Code */ + static func main() { + /* 初始化无向图 */ + let v0 = Vertex(val: 1) + let v1 = Vertex(val: 3) + let v2 = Vertex(val: 2) + let v3 = Vertex(val: 5) + let v4 = Vertex(val: 4) + let edges = [[v0, v1], [v1, v2], [v2, v3], [v0, v3], [v2, v4], [v3, v4]] + let graph = GraphAdjList(edges: edges) + print("\n初始化后,图为") + graph.print() + + /* 添加边 */ + // 顶点 1, 2 即 v0, v2 + graph.addEdge(vet1: v0, vet2: v2) + print("\n添加边 1-2 后,图为") + graph.print() + + /* 删除边 */ + // 顶点 1, 3 即 v0, v1 + graph.removeEdge(vet1: v0, vet2: v1) + print("\n删除边 1-3 后,图为") + graph.print() + + /* 添加顶点 */ + let v5 = Vertex(val: 6) + graph.addVertex(vet: v5) + print("\n添加顶点 6 后,图为") + graph.print() + + /* 删除顶点 */ + // 顶点 3 即 v1 + graph.removeVertex(vet: v1) + print("\n删除顶点 3 后,图为") + graph.print() + } +} diff --git a/codes/swift/chapter_graph/graph_adjacency_matrix.swift b/codes/swift/chapter_graph/graph_adjacency_matrix.swift new file mode 100644 index 000000000..34321fd34 --- /dev/null +++ b/codes/swift/chapter_graph/graph_adjacency_matrix.swift @@ -0,0 +1,130 @@ +/** + * File: graph_adjacency_matrix.swift + * Created Time: 2023-02-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* 基于邻接矩阵实现的无向图类 */ +class GraphAdjMat { + private var vertices: [Int] // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + private var adjMat: [[Int]] // 邻接矩阵,行列索引对应“顶点索引” + + /* 构造函数 */ + init(vertices: [Int], edges: [[Int]]) { + self.vertices = [] + adjMat = [] + // 添加顶点 + for val in vertices { + addVertex(val: val) + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for e in edges { + addEdge(i: e[0], j: e[1]) + } + } + + /* 获取顶点数量 */ + func size() -> Int { + vertices.count + } + + /* 添加顶点 */ + func addVertex(val: Int) { + let n = size() + // 向顶点列表中添加新顶点的值 + vertices.append(val) + // 在邻接矩阵中添加一行 + let newRow = Array(repeating: 0, count: n) + adjMat.append(newRow) + // 在邻接矩阵中添加一列 + for i in adjMat.indices { + adjMat[i].append(0) + } + } + + /* 删除顶点 */ + func removeVertex(index: Int) { + if index >= size() { + fatalError("越界") + } + // 在顶点列表中移除索引 index 的顶点 + vertices.remove(at: index) + // 在邻接矩阵中删除索引 index 的行 + adjMat.remove(at: index) + // 在邻接矩阵中删除索引 index 的列 + for i in adjMat.indices { + adjMat[i].remove(at: index) + } + } + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + func addEdge(i: Int, j: Int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= size() || j >= size() || i == j { + fatalError("越界") + } + // 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i) + adjMat[i][j] = 1 + adjMat[j][i] = 1 + } + + /* 删除边 */ + // 参数 i, j 对应 vertices 元素索引 + func removeEdge(i: Int, j: Int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= size() || j >= size() || i == j { + fatalError("越界") + } + adjMat[i][j] = 0 + adjMat[j][i] = 0 + } + + /* 打印邻接矩阵 */ + func print() { + Swift.print("顶点列表 = ", terminator: "") + Swift.print(vertices) + Swift.print("邻接矩阵 =") + PrintUtil.printMatrix(matrix: adjMat) + } +} + +@main +enum GraphAdjacencyMatrix { + /* Driver Code */ + static func main() { + /* 初始化无向图 */ + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + let vertices = [1, 3, 2, 5, 4] + let edges = [[0, 1], [0, 2], [1, 2], [2, 3], [0, 3], [2, 4], [3, 4]] + let graph = GraphAdjMat(vertices: vertices, edges: edges) + print("\n初始化后,图为") + graph.print() + + /* 添加边 */ + // 顶点 1, 2 的索引分别为 0, 2 + graph.addEdge(i: 0, j: 2) + print("\n添加边 1-2 后,图为") + graph.print() + + /* 删除边 */ + // 顶点 1, 3 的索引分别为 0, 1 + graph.removeEdge(i: 0, j: 1) + print("\n删除边 1-3 后,图为") + graph.print() + + /* 添加顶点 */ + graph.addVertex(val: 6) + print("\n添加顶点 6 后,图为") + graph.print() + + /* 删除顶点 */ + // 顶点 3 的索引为 1 + graph.removeVertex(index: 1) + print("\n删除顶点 3 后,图为") + graph.print() + } +} diff --git a/codes/swift/utils/PrintUtil.swift b/codes/swift/utils/PrintUtil.swift index 24b0fe840..384c96199 100644 --- a/codes/swift/utils/PrintUtil.swift +++ b/codes/swift/utils/PrintUtil.swift @@ -82,4 +82,12 @@ public enum PrintUtil { let root = TreeNode.listToTree(list: queue) printTree(root: root) } + + public static func printMatrix(matrix: [[T]]) { + print("[") + for row in matrix { + print(" \(row),") + } + print("]") + } } diff --git a/docs/chapter_graph/graph_operations.md b/docs/chapter_graph/graph_operations.md index 50b05f2eb..3ce7f122d 100644 --- a/docs/chapter_graph/graph_operations.md +++ b/docs/chapter_graph/graph_operations.md @@ -159,7 +159,83 @@ comments: true === "Swift" ```swift title="graph_adjacency_matrix.swift" + /* 基于邻接矩阵实现的无向图类 */ + class GraphAdjMat { + private var vertices: [Int] // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + private var adjMat: [[Int]] // 邻接矩阵,行列索引对应“顶点索引” + /* 构造函数 */ + init(vertices: [Int], edges: [[Int]]) { + self.vertices = [] + adjMat = [] + // 添加顶点 + for val in vertices { + addVertex(val: val) + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for e in edges { + addEdge(i: e[0], j: e[1]) + } + } + + /* 获取顶点数量 */ + func size() -> Int { + vertices.count + } + + /* 添加顶点 */ + func addVertex(val: Int) { + let n = size() + // 向顶点列表中添加新顶点的值 + vertices.append(val) + // 在邻接矩阵中添加一行 + let newRow = Array(repeating: 0, count: n) + adjMat.append(newRow) + // 在邻接矩阵中添加一列 + for i in adjMat.indices { + adjMat[i].append(0) + } + } + + /* 删除顶点 */ + func removeVertex(index: Int) { + if index >= size() { + fatalError("越界") + } + // 在顶点列表中移除索引 index 的顶点 + vertices.remove(at: index) + // 在邻接矩阵中删除索引 index 的行 + adjMat.remove(at: index) + // 在邻接矩阵中删除索引 index 的列 + for i in adjMat.indices { + adjMat[i].remove(at: index) + } + } + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + func addEdge(i: Int, j: Int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= size() || j >= size() || i == j { + fatalError("越界") + } + // 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i) + adjMat[i][j] = 1 + adjMat[j][i] = 1 + } + + /* 删除边 */ + // 参数 i, j 对应 vertices 元素索引 + func removeEdge(i: Int, j: Int) { + // 索引越界与相等处理 + if i < 0 || j < 0 || i >= size() || j >= size() || i == j { + fatalError("越界") + } + adjMat[i][j] = 0 + adjMat[j][i] = 0 + } + } ``` === "Zig" @@ -312,7 +388,85 @@ comments: true === "Swift" ```swift title="graph_adjacency_list.swift" + /* 顶点类 */ + class Vertex: Hashable { + var val: Int + init(val: Int) { + self.val = val + } + + static func == (lhs: Vertex, rhs: Vertex) -> Bool { + lhs.val == rhs.val + } + + func hash(into hasher: inout Hasher) { + hasher.combine(val) + } + } + + /* 基于邻接表实现的无向图类 */ + class GraphAdjList { + // 请注意,vertices 和 adjList 中存储的都是 Vertex 对象 + private var adjList: [Vertex: Set] // 邻接表(使用哈希表实现) + + init(edges: [[Vertex]]) { + adjList = [:] + // 添加所有顶点和边 + for edge in edges { + addVertex(vet: edge[0]) + addVertex(vet: edge[1]) + addEdge(vet1: edge[0], vet2: edge[1]) + } + } + + /* 获取顶点数量 */ + func size() -> Int { + adjList.count + } + + /* 添加边 */ + func addEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("参数错误") + } + // 添加边 vet1 - vet2 + adjList[vet1]?.insert(vet2) + adjList[vet2]?.insert(vet1) + } + + /* 删除边 */ + func removeEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("参数错误") + } + // 删除边 vet1 - vet2 + adjList[vet1]?.remove(vet2) + adjList[vet2]?.remove(vet1) + } + + /* 添加顶点 */ + func addVertex(vet: Vertex) { + if adjList[vet] != nil { + return + } + // 在邻接表中添加一个新链表(即 HashSet) + adjList[vet] = [] + } + + /* 删除顶点 */ + func removeVertex(vet: Vertex) { + if adjList[vet] == nil { + fatalError("参数错误") + } + // 在邻接表中删除顶点 vet 对应的链表(即 HashSet) + adjList.removeValue(forKey: vet) + // 遍历其它顶点的链表(即 HashSet),删除所有包含 vet 的边 + for key in adjList.keys { + adjList[key]?.remove(vet) + } + } + } ``` === "Zig"