[图] 遍历 | BFS | DFS

ops/2024/12/21 19:00:23/

目录

1. 基本概念

2. 图的广度优先遍历(BFS)

3. 图的深度优先遍历(DFS)

4. 非连通图的遍历


1. 基本概念

给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶点仅被遍历一次。"遍历"即对顶点进行某种操作的意思。

在前面的 树 里面 形象的总结过:

  • DFS 是线,BFS 是面;DFS 是单打独斗,BFS 是集体行动

2. 图的广度优先遍历(BFS)

  • 借助队列实现:广度优先遍历依赖于队列来实现逐层遍历。
  • 遍历过程
    • 以某个顶点为起点,当这个顶点出队列后,将与之邻接的所有顶点入队列。
    • 每次处理一层顶点,然后继续向外扩展

  • 避免死循环:使用bool类型的数组标记已经访问过的顶点,防止重复入队列。
void BFS(const V& src)
{size_t srci = GetVertexindex(src);size_t n = _vertexs.size();queue<int> q;vector<bool> vis(n, false);q.push(srci);vis[srci] = true;while (!q.empty()){size_t front = q.front();q.pop();cout << front << ":" << _vertexs[front] << endl;for (size_t i = 0; i < n; ++i){if (_matrix[front][i] != MAX_W && !vis[i]){q.push(i);vis[i] = true;}}}
}

图解:

  • 有树的基础就知道广度优先遍历必然要借助 队列 来实现。
  • 广度优先遍历就是以某个点为起点,当这个顶点出队列就把和这个顶点的 邻接顶点都入队列,然后一层一层往外走。
  • 但是要注意的是 已经入过队列的顶点下一次不能在入队列,否则就会死循环,因此还要来一个标记bool类型数组。当一个顶点入队列后就标记一下。

问题:

错误 C2995 “size_t Graph<V,W>::GetVertexindex(const V &)”: 函数模板已经定义

测试:

应用实例

  • 例如找到六度好友的问题,可以利用BFS的一层一层遍历特性,通过计算队列内元素个数确保分层处理。

代码:

 void RecommendFriends(const V& src, size_t max_degree = 6, size_t max_recommendations = 10) {try {size_t srci = GetVertexindex(src);size_t n = _vertexs.size();queue<pair<size_t, size_t>> q; // 存储顶点索引及其对应的度数vector<bool> vis(n, false);    // 访问标记数组vector<V> recommended_friends; // 推荐好友列表q.push(make_pair(srci, 0));vis[srci] = true;while (!q.empty() && recommended_friends.size() < max_recommendations) {size_t k = q.size();while (k--) {pair<size_t, size_t> front_level = q.front();q.pop();size_t front = front_level.first;size_t level = front_level.second;// 不打印起点本身if (level > 0 && level <= max_degree) {cout << "Degree " << level << ": " << _vertexs[front] << endl;recommended_friends.push_back(_vertexs[front]);if (recommended_friends.size() >= max_recommendations) break;}for (size_t i = 0; i < n; ++i) {if (_matrix[front][i] != MAX_W && !vis[i]) {q.push(make_pair(i, level + 1));vis[i] = true;}}}}

回想一下刚才我们的BFS顶点出队列是怎么出的?是一个一个出的。

对于这道题我们 出队列的就要求一层一层出。那怎么一层一层出呢?很简单出队列之前计算一下当前队列内元素的个数。每次出队列内元素个数次。


3. 图的深度优先遍历(DFS)

上图中,红色箭头是递归,绿色箭头是回溯。

深度优先遍历,就是优先将邻接顶点的所有其它邻接顶点都遍历完,再遍历下一个邻接顶点,其实和树是一样的。

  • 比如对于F顶点,其有三个连通顶点,除去CF先遍历,F首先遍历D连通的所有顶点,但是D连通的顶点都被遍历过了,所以没有继续深入。
  • 当绿色箭头返回到F,说明和D连通的所有顶点都被遍历完了,此时再去遍历HH有一个连通的顶点I,所以先去遍历I,再回到F
  • 唯一不同的是,树有固定的根节点,每次都从根结点开始遍历,而图没有固定的根,用户传入任意一个顶点都可以开始遍历。
void _DFS(size_t srci, const size_t& n, vector<bool>& vis)
{cout << srci << ":" << _vertexs[srci] << endl;vis[srci] = true;for (size_t i = 0; i < n; ++i){if (_matrix[srci][i] != MAX_W && !vis[i]){_DFS(i, n, vis);}}
}void DFS(const V& src)
{size_t srci = GetVertexindex(src);size_t n = _vertexs.size();vector<bool> vis(n, false);_DFS(srci, n, vis);
}

DFS接口接收一个src,从该顶点开始遍历,将遍历结果按顺序放在数组ret中进行返回。

在递归本体_DFS中,每递归一个节点,将当前节点visited[srci] = true,表示已经遍历过,防止重复遍历。

  • !visited[i] && _matrix[srci][i] != MAX_W:只要下标i对应的顶点没有遍历过,并且和当前的srci是连通的,就进行递归遍历。

4. 非连通图的遍历

  • 如果图不是完全连通的,那么从单一顶点开始的BFS或DFS可能无法访问到所有顶点。

其实很简单,不是有标记数组吗。

把标记数组在遍历一遍,如果还有顶点没有被遍历到那就以这个顶点在做一次BFS或DFS

以 bfs 为例:

// 遍历非连通图中的所有连通分量void TraverseAllComponents() {size_t n = _vertexs.size();vector<bool> visited(n, false);for (size_t i = 0; i < n; ++i) {if (!visited[i]) {cout << "Starting new component from vertex: " << _vertexs[i] << endl;BFSComponent(i, visited);}}}// 广度优先搜索遍历单个连通分量void BFSComponent(size_t start, vector<bool>& visited) {size_t n = _vertexs.size();queue<size_t> q; // 存储顶点索引q.push(start);visited[start] = true;while (!q.empty()) {size_t front = q.front();q.pop();cout << "Visited: " << _vertexs[front] << endl;for (size_t i = 0; i < n; ++i) {if (_matrix[front][i] != MAX_W && !visited[i]) {q.push(i);visited[i] = true;}}}}
};

http://www.ppmy.cn/ops/143833.html

相关文章

【练习Day17】寻找第 K 大

链接&#xff1a;寻找第K大_牛客题霸_牛客网 方法&#xff1a;快排二分查找&#xff08;推荐使用&#xff09; 知识点&#xff1a;分治 分治即“分而治之”&#xff0c;“分”指的是将一个大而复杂的问题划分成多个性质相同但是规模更小的子问题&#xff0c;子问题继续按照这…

netfilter简介及流程图

Netfilter 是 Linux 内核中用于网络包过滤和操作的框架&#xff0c;由 Rusty Russell 于1998年创立&#xff0c;旨在改进旧的 ipchains 和 ipfwadm 实现。它采用模块化设计&#xff0c;具有良好可扩展性&#xff0c;并在2000年3月合并进Linux 2.3.x内核版本。 Netfilter的主要…

ElasticSearch学习7

SpringBoot整合文档相关的操作 1、文档的添加 // 测试添加文档(先创建一个User实体类&#xff0c;添加fastjson依赖) Test public void testAddDocument() throws IOException { // 创建一个User对象 User liuyou new User("liuyou", 18); // 创建请求 …

电力场景电力部件热点区域检测数据集VOC+YOLO格式183张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;183 标注数量(xml文件个数)&#xff1a;183 标注数量(txt文件个数)&#xff1a;183 标注…

基础入门-Web应用蜜罐系统堡垒机运维API内外接口第三方拓展架构部署影响

知识点&#xff1a; 1、基础入门-Web应用-蜜罐系统 2、基础入门-Web应用-堡垒机运维 3、基础入门-Web应用-内外API接口 4、基础入门-Web应用-第三方拓展架构 一、演示案例-Web-拓展应用-蜜罐-钓鱼诱使 蜜罐&#xff1a;https://hfish.net/ 测试系统&#xff1a;Ubuntu 20.04 …

四、CSS3

一、CSS3简介 1、CSS3概述 CSS3 是 CSS2 的升级版本&#xff0c;他在CSS2的基础上&#xff0c;新增了很多强大的新功能&#xff0c;从而解决一些实际面临的问题。 CSS在未来会按照模块化的方式去发展&#xff1a;https://www.w3.org/Style/CSS/current-work.html …

【docker】如何打包前端并运行

前端使用 Vue 3 Vite 1.use npm run preview 运行 0.项目根目录下新建.env文件 VITE_BASE_API_prodhttp://127.0.0.1:5000/api # 线上环境 VITE_MOCK_API_prodapi # 本地模拟数据 VITE_BASE_API_devhttp://127.0.0.1:5000/ap…

【机器学习】机器学习的基本分类-强化学习-REINFORCE 算法

REINFORCE 算法 REINFORCE 是一种基于策略梯度的强化学习算法&#xff0c;直接通过采样环境中的轨迹来优化策略。它是策略梯度方法的基础实现&#xff0c;具有简单直观的优点。 核心思想 目标函数 最大化策略的期望回报&#xff1a; ​​​​​​​ …