mirror of
https://github.com/krahets/hello-algo.git
synced 2025-02-02 14:18:47 +08:00
feat: Add C++ code for the graph bfs and dfs (#401)
* Add C++ code for the graph bfs and dfs * Add C++ code for the graph bfs and dfs
This commit is contained in:
parent
4f941e3d99
commit
33c797efeb
@ -1,24 +1,27 @@
|
||||
/**
|
||||
* File: graph_adjacency_list.cpp
|
||||
* Created Time: 2023-02-09
|
||||
* Author: what-is-me (whatisme@outlook.jp)
|
||||
* Author: what-is-me (whatisme@outlook.jp), Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.hpp"
|
||||
|
||||
/* 顶点类 */
|
||||
struct Vertex {
|
||||
int val;
|
||||
Vertex(int val) : val(val) {}
|
||||
};
|
||||
|
||||
/* 基于邻接表实现的无向图类 */
|
||||
class GraphAdjList {
|
||||
// 邻接表,使用哈希表来代替链表,以提升删除边、删除顶点的效率
|
||||
// 请注意,adjList 中的元素是 Vertex 对象
|
||||
unordered_map<Vertex*, unordered_set<Vertex*>> adjList;
|
||||
|
||||
public:
|
||||
// 邻接表,key: 顶点,value:该顶点的所有邻接顶点
|
||||
unordered_map<Vertex*, vector<Vertex*>> adjList;
|
||||
|
||||
/* 在 vector 中删除指定结点 */
|
||||
void remove(vector<Vertex*> &vec, Vertex *vet) {
|
||||
for (int i = 0; i < vec.size(); i++) {
|
||||
if (vec[i] == vet) {
|
||||
vec.erase(vec.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 构造方法 */
|
||||
GraphAdjList(const vector<vector<Vertex*>>& edges) {
|
||||
// 添加所有顶点和边
|
||||
@ -37,8 +40,8 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
/* 删除边 */
|
||||
@ -46,15 +49,15 @@ public:
|
||||
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<Vertex*>();
|
||||
adjList[vet] = vector<Vertex*>();
|
||||
}
|
||||
|
||||
/* 删除顶点 */
|
||||
@ -64,67 +67,19 @@ public:
|
||||
// 在邻接表中删除顶点 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<int> 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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化无向图 */
|
||||
Vertex *v0 = new Vertex(1),
|
||||
*v1 = new Vertex(3),
|
||||
*v2 = new Vertex(2),
|
||||
*v3 = new Vertex(5),
|
||||
*v4 = new Vertex(4);
|
||||
vector<vector<Vertex*>> edges = {{v0, v1}, {v1, v2}, {v2, v3}, {v0, v3}, {v2, v4}, {v3, v4}};
|
||||
GraphAdjList graph(edges);
|
||||
cout << "\n初始化后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 添加边 */
|
||||
// 顶点 1, 2 即 v0, v2
|
||||
graph.addEdge(v0, v2);
|
||||
cout << "\n添加边 1-2 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 删除边 */
|
||||
// 顶点 1, 3 即 v0, v1
|
||||
graph.removeEdge(v0, v1);
|
||||
cout << "\n删除边 1-3 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 添加顶点 */
|
||||
Vertex* v5 = new Vertex(6);
|
||||
graph.addVertex(v5);
|
||||
cout << "\n添加顶点 6 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 删除顶点 */
|
||||
// 顶点 3 即 v1
|
||||
graph.removeVertex(v1);
|
||||
cout << "\n删除顶点 3 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 释放内存 */
|
||||
delete v0;
|
||||
delete v1;
|
||||
delete v2;
|
||||
delete v3;
|
||||
delete v4;
|
||||
delete v5;
|
||||
}
|
||||
// GraphAdjList 的测试样例在 graph_adjacency_list_test.cpp 文件中
|
||||
|
49
codes/cpp/chapter_graph/graph_adjacency_list_test.cpp
Normal file
49
codes/cpp/chapter_graph/graph_adjacency_list_test.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: graph_adjacency_list_test.cpp
|
||||
* Created Time: 2023-02-09
|
||||
* Author: what-is-me (whatisme@outlook.jp), Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "./graph_adjacency_list.cpp"
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化无向图 */
|
||||
vector<Vertex*> v = valsToVets(vector<int> { 1, 3, 2, 5, 4 });
|
||||
vector<vector<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(edges);
|
||||
cout << "\n初始化后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 添加边 */
|
||||
// 顶点 1, 2 即 v[0], v[2]
|
||||
graph.addEdge(v[0], v[2]);
|
||||
cout << "\n添加边 1-2 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 删除边 */
|
||||
// 顶点 1, 3 即 v[0], v[1]
|
||||
graph.removeEdge(v[0], v[1]);
|
||||
cout << "\n删除边 1-3 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 添加顶点 */
|
||||
Vertex* v5 = new Vertex(6);
|
||||
graph.addVertex(v5);
|
||||
cout << "\n添加顶点 6 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 删除顶点 */
|
||||
// 顶点 3 即 v[1]
|
||||
graph.removeVertex(v[1]);
|
||||
cout << "\n删除顶点 3 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
// 释放内存
|
||||
for (Vertex *vet : v) {
|
||||
delete vet;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -8,8 +8,8 @@
|
||||
|
||||
/* 基于邻接矩阵实现的无向图类 */
|
||||
class GraphAdjMat {
|
||||
vector<int> vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
|
||||
vector<vector<int>> adjMat; // 邻接矩阵,行列索引对应“顶点索引”
|
||||
vector<int> vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
|
||||
vector<vector<int>> adjMat; // 邻接矩阵,行列索引对应“顶点索引”
|
||||
|
||||
public:
|
||||
/* 构造方法 */
|
||||
@ -90,13 +90,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化无向图 */
|
||||
// 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引
|
||||
vector<int> vertices = {1, 3, 2, 5, 4};
|
||||
vector<vector<int>> edges = {{0, 1}, {0, 2}, {1, 2}, {2, 3}, {0, 3}, {2, 4}, {3, 4}};
|
||||
vector<int> vertices = { 1, 3, 2, 5, 4 };
|
||||
vector<vector<int>> edges = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 2, 3 }, { 2, 4 }, { 3, 4 } };
|
||||
GraphAdjMat graph(vertices, edges);
|
||||
cout << "\n初始化后,图为" << endl;
|
||||
graph.print();
|
||||
@ -123,4 +122,6 @@ int main() {
|
||||
graph.removeVertex(1);
|
||||
cout << "\n删除顶点 3 后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
59
codes/cpp/chapter_graph/graph_bfs.cpp
Normal file
59
codes/cpp/chapter_graph/graph_bfs.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* File: graph_bfs.cpp
|
||||
* Created Time: 2023-03-02
|
||||
* Author: Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.hpp"
|
||||
#include "./graph_adjacency_list.cpp"
|
||||
|
||||
/* 广度优先遍历 BFS */
|
||||
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
|
||||
vector<Vertex*> graphBFS(GraphAdjList &graph, Vertex *startVet) {
|
||||
// 顶点遍历序列
|
||||
vector<Vertex*> res;
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
unordered_set<Vertex*> visited = { startVet };
|
||||
// 队列用于实现 BFS
|
||||
queue<Vertex*> 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;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化无向图 */
|
||||
vector<Vertex*> v = valsToVets({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
vector<vector<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(edges);
|
||||
cout << "\n初始化后,图为\\n";
|
||||
graph.print();
|
||||
|
||||
/* 广度优先遍历 BFS */
|
||||
vector<Vertex*> res = graphBFS(graph, v[0]);
|
||||
cout << "\n广度优先遍历(BFS)顶点序列为" << endl;
|
||||
PrintUtil::printVector(vetsToVals(res));
|
||||
|
||||
// 释放内存
|
||||
for (Vertex *vet : v) {
|
||||
delete vet;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
55
codes/cpp/chapter_graph/graph_dfs.cpp
Normal file
55
codes/cpp/chapter_graph/graph_dfs.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: graph_dfs.cpp
|
||||
* Created Time: 2023-03-02
|
||||
* Author: Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.hpp"
|
||||
#include "./graph_adjacency_list.cpp"
|
||||
|
||||
/* 深度优先遍历 DFS 辅助函数 */
|
||||
void dfs(GraphAdjList& graph, unordered_set<Vertex*>& visited, vector<Vertex*>& 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 深度优先遍历 DFS */
|
||||
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
|
||||
vector<Vertex*> graphDFS(GraphAdjList& graph, Vertex* startVet) {
|
||||
// 顶点遍历序列
|
||||
vector<Vertex*> res;
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
unordered_set<Vertex*> visited;
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化无向图 */
|
||||
vector<Vertex*> v = valsToVets(vector<int> { 0, 1, 2, 3, 4, 5, 6 });
|
||||
vector<vector<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(edges);
|
||||
cout << "\n初始化后,图为" << endl;
|
||||
graph.print();
|
||||
|
||||
/* 深度优先遍历 DFS */
|
||||
vector<Vertex*> res = graphDFS(graph, v[0]);
|
||||
cout << "\n深度优先遍历(DFS)顶点序列为" << endl;
|
||||
PrintUtil::printVector(vetsToVals(res));
|
||||
|
||||
// 释放内存
|
||||
for (Vertex *vet : v) {
|
||||
delete vet;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -8,17 +8,17 @@
|
||||
|
||||
|
||||
/* 双向链表结点 */
|
||||
struct DoubleListNode {
|
||||
struct DoublyListNode {
|
||||
int val; // 结点值
|
||||
DoubleListNode *next; // 后继结点指针
|
||||
DoubleListNode *prev; // 前驱结点指针
|
||||
DoubleListNode(int val) : val(val), prev(nullptr), next(nullptr) {}
|
||||
DoublyListNode *next; // 后继结点指针
|
||||
DoublyListNode *prev; // 前驱结点指针
|
||||
DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) {}
|
||||
};
|
||||
|
||||
/* 基于双向链表实现的双向队列 */
|
||||
class LinkedListDeque {
|
||||
private:
|
||||
DoubleListNode *front, *rear; // 头结点 front ,尾结点 rear
|
||||
DoublyListNode *front, *rear; // 头结点 front ,尾结点 rear
|
||||
int queSize = 0; // 双向队列的长度
|
||||
|
||||
public:
|
||||
@ -28,7 +28,7 @@ public:
|
||||
/* 析构方法 */
|
||||
~LinkedListDeque() {
|
||||
// 释放内存
|
||||
DoubleListNode *pre, *cur = front;
|
||||
DoublyListNode *pre, *cur = front;
|
||||
while (cur != nullptr) {
|
||||
pre = cur;
|
||||
cur = cur->next;
|
||||
@ -48,7 +48,7 @@ public:
|
||||
|
||||
/* 入队操作 */
|
||||
void push(int num, bool isFront) {
|
||||
DoubleListNode *node = new DoubleListNode(num);
|
||||
DoublyListNode *node = new DoublyListNode(num);
|
||||
// 若链表为空,则令 front, rear 都指向 node
|
||||
if (isEmpty())
|
||||
front = rear = node;
|
||||
@ -88,7 +88,7 @@ public:
|
||||
if (isFront) {
|
||||
val = front->val; // 暂存头结点值
|
||||
// 删除头结点
|
||||
DoubleListNode *fNext = front->next;
|
||||
DoublyListNode *fNext = front->next;
|
||||
if (fNext != nullptr) {
|
||||
fNext->prev = nullptr;
|
||||
front->next = nullptr;
|
||||
@ -98,7 +98,7 @@ public:
|
||||
} else {
|
||||
val = rear->val; // 暂存尾结点值
|
||||
// 删除尾结点
|
||||
DoubleListNode *rPrev = rear->prev;
|
||||
DoublyListNode *rPrev = rear->prev;
|
||||
if (rPrev != nullptr) {
|
||||
rPrev->next = nullptr;
|
||||
rear->prev = nullptr;
|
||||
@ -131,7 +131,7 @@ public:
|
||||
|
||||
/* 返回数组用于打印 */
|
||||
vector<int> toVector() {
|
||||
DoubleListNode *node = front;
|
||||
DoublyListNode *node = front;
|
||||
vector<int> res(size());
|
||||
for (int i = 0; i < res.size(); i++) {
|
||||
res[i] = node->val;
|
||||
|
34
codes/cpp/include/Vertex.hpp
Normal file
34
codes/cpp/include/Vertex.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* File: PrintUtil.hpp
|
||||
* Created Time: 2023-03-02
|
||||
* Author: Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
/* 顶点类 */
|
||||
struct Vertex {
|
||||
int val;
|
||||
Vertex(int x) : val(x) {}
|
||||
};
|
||||
|
||||
/* 输入值列表 vals ,返回顶点列表 vets */
|
||||
vector<Vertex*> valsToVets(vector<int> vals) {
|
||||
vector<Vertex*> vets;
|
||||
for (int val : vals) {
|
||||
vets.push_back(new Vertex(val));
|
||||
}
|
||||
return vets;
|
||||
}
|
||||
|
||||
/* 输入顶点列表 vets ,返回值列表 vals */
|
||||
vector<int> vetsToVals(vector<Vertex*> vets) {
|
||||
vector<int> vals;
|
||||
for (Vertex *vet : vets) {
|
||||
vals.push_back(vet->val);
|
||||
}
|
||||
return vals;
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "ListNode.hpp"
|
||||
#include "TreeNode.hpp"
|
||||
#include "Vertex.hpp"
|
||||
#include "PrintUtil.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
@ -84,7 +84,8 @@ public class graph_adjacency_list {
|
||||
public static void main(String[] args) {
|
||||
/* 初始化无向图 */
|
||||
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] } };
|
||||
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();
|
||||
|
@ -325,7 +325,7 @@
|
||||
=== "C++"
|
||||
|
||||
```cpp title="linkedlist_deque.cpp"
|
||||
[class]{ListNode}-[func]{}
|
||||
[class]{DoublyListNode}-[func]{}
|
||||
|
||||
[class]{LinkedListDeque}-[func]{}
|
||||
```
|
||||
|
@ -64,8 +64,6 @@ hide:
|
||||
|
||||
如果你也有上述烦恼,那么很幸运这本书找到了你。本书是我对于该问题给出的答案,虽然不一定正确,但至少代表一次积极的尝试。这本书虽然不足以让你直接拿到 Offer ,但会引导你探索数据结构与算法的“知识地图”,带你了解不同“地雷”的形状大小和分布位置,让你掌握各种“排雷方法”。有了这些本领,相信你可以更加得心应手地刷题与阅读文献,逐步搭建起完整的知识体系。
|
||||
|
||||
书中的代码均配有可一键运行的源文件,托管在 [github.com/krahets/hello-algo](https://github.com/krahets/hello-algo) 仓库,建议前往下载。发行版 PDF 的更新周期较长,想看最新内容的小伙伴可以前往 [www.hello-algo.com](https://www.hello-algo.com/) 网页版。
|
||||
|
||||
<h3 align="left"> 作者简介 </h3>
|
||||
|
||||
靳宇栋 (Krahets),大厂高级算法工程师,上海交通大学硕士。力扣(LeetCode)全网阅读量最高博主,其 LeetBook《图解算法数据结构》已被订阅 22 万本。
|
||||
|
Loading…
Reference in New Issue
Block a user