From 851107c4ebc57f84c0a0831eb2d199a634868ea0 Mon Sep 17 00:00:00 2001 From: krahets Date: Thu, 2 Mar 2023 18:49:17 +0800 Subject: [PATCH] build --- chapter_graph/graph_operations.md | 38 +++++++++++++---------- chapter_graph/graph_traversal.md | 51 +++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/chapter_graph/graph_operations.md b/chapter_graph/graph_operations.md index 7fe7530f3..f4ba309ba 100644 --- a/chapter_graph/graph_operations.md +++ b/chapter_graph/graph_operations.md @@ -886,11 +886,20 @@ comments: true ```cpp title="graph_adjacency_list.cpp" /* 基于邻接表实现的无向图类 */ class GraphAdjList { - // 邻接表,使用哈希表来代替链表,以提升删除边、删除顶点的效率 - // 请注意,adjList 中的元素是 Vertex 对象 - unordered_map> adjList; - public: + // 邻接表,key: 顶点,value:该顶点的所有邻接顶点 + unordered_map> adjList; + + /* 在 vector 中删除指定结点 */ + void remove(vector &vec, Vertex *vet) { + for (int i = 0; i < vec.size(); i++) { + if (vec[i] == vet) { + vec.erase(vec.begin() + i); + break; + } + } + } + /* 构造方法 */ GraphAdjList(const vector>& edges) { // 添加所有顶点和边 @@ -909,8 +918,8 @@ comments: true if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) throw invalid_argument("不存在顶点"); // 添加边 vet1 - vet2 - adjList[vet1].insert(vet2); - adjList[vet2].insert(vet1); + adjList[vet1].push_back(vet2); + adjList[vet2].push_back(vet1); } /* 删除边 */ @@ -918,15 +927,15 @@ comments: true if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) throw invalid_argument("不存在顶点"); // 删除边 vet1 - vet2 - adjList[vet1].erase(vet2); - adjList[vet2].erase(vet1); + remove(adjList[vet1], vet2); + remove(adjList[vet2], vet1); } /* 添加顶点 */ void addVertex(Vertex* vet) { if (adjList.count(vet)) return; // 在邻接表中添加一个新链表 - adjList[vet] = unordered_set(); + adjList[vet] = vector(); } /* 删除顶点 */ @@ -936,20 +945,17 @@ comments: true // 在邻接表中删除顶点 vet 对应的链表 adjList.erase(vet); // 遍历其它顶点的链表,删除所有包含 vet 的边 - for (auto& [key, set_] : adjList) { - set_.erase(vet); + for (auto& [key, vec] : adjList) { + remove(vec, vet); } } /* 打印邻接表 */ void print() { cout << "邻接表 =" << endl; - for (auto& [key, value] : adjList) { - vector tmp; - for (Vertex* vertex : value) - tmp.push_back(vertex->val); + for (auto& [key, vec] : adjList) { cout << key->val << ": "; - PrintUtil::printVector(tmp); + PrintUtil::printVector(vetsToVals(vec)); } } }; diff --git a/chapter_graph/graph_traversal.md b/chapter_graph/graph_traversal.md index 22f645e85..0c30f89c9 100644 --- a/chapter_graph/graph_traversal.md +++ b/chapter_graph/graph_traversal.md @@ -62,7 +62,32 @@ BFS 常借助「队列」来实现。队列具有“先入先出”的性质, === "C++" ```cpp title="graph_bfs.cpp" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + vector graphBFS(GraphAdjList &graph, Vertex *startVet) { + // 顶点遍历序列 + vector res; + // 哈希表,用于记录已被访问过的顶点 + unordered_set visited = { startVet }; + // 队列用于实现 BFS + queue que; + que.push(startVet); + // 以顶点 vet 为起点,循环直至访问完所有顶点 + while (!que.empty()) { + Vertex *vet = que.front(); + que.pop(); // 队首顶点出队 + res.push_back(vet); // 记录访问顶点 + // 遍历该顶点的所有邻接顶点 + for (auto adjVet : graph.adjList[vet]) { + if (visited.count(adjVet)) + continue; // 跳过已被访问过的顶点 + que.push(adjVet); // 只入队未访问的顶点 + visited.emplace(adjVet); // 标记该顶点已被访问 + } + } + // 返回顶点遍历序列 + return res; + } ``` === "Python" @@ -325,9 +350,29 @@ BFS 常借助「队列」来实现。队列具有“先入先出”的性质, === "C++" ```cpp title="graph_dfs.cpp" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + void dfs(GraphAdjList& graph, unordered_set& visited, vector& res, Vertex* vet) { + res.push_back(vet); // 记录访问顶点 + visited.emplace(vet); // 标记该顶点已被访问 + // 遍历该顶点的所有邻接顶点 + for (Vertex* adjVet : graph.adjList[vet]) { + if (visited.count(adjVet)) + continue; // 跳过已被访问过的顶点 + // 递归访问邻接顶点 + dfs(graph, visited, res, adjVet); + } + } - [class]{}-[func]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + vector graphDFS(GraphAdjList& graph, Vertex* startVet) { + // 顶点遍历序列 + vector res; + // 哈希表,用于记录已被访问过的顶点 + unordered_set visited; + dfs(graph, visited, res, startVet); + return res; + } ``` === "Python"