数据结构:图的遍历--深度优先、广度优先

article/2025/9/17 4:45:58

                       图的遍历:深度优先、广度优先

遍历

    图的遍历是指从图中的某一顶点出发,按照一定的策略访问图中的每一个顶点。当然,每个顶点有且只能被访问一次。

    在图的遍历中,深度优先和广度优先是最常使用的两种遍历方式。这两种遍历方式对无向图和有向图都是适用的,并且都是从指定的顶点开始遍历的。先看下两种遍历方式的遍历规则:

深度优先

    深度优先遍历也叫深度优先搜索(Depth First Search)。它的遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。

    具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,则下一个遍历的顶点是V0第一个邻接点Vi,接着遍历Vi第一个邻接点Vj,……直到所有的顶点都被访问过。

    所谓的第一个是指在某种存储结构中(邻接矩阵、邻接表),所有邻接点中存储位置最近的,通常指的是下标最小的。在遍历的过程中有两种情况经常出现

  1. 某个顶点的邻接点都已被访问过的情况,此时需回溯已访问过的顶点。
  2. 图不连通,所有的已访问过的顶点都已回溯完了,仍找不出未被访问的顶点。此时需从下标0开始检测visited[i],找到未被访问的顶点i,从i开始新一轮的深度搜索。
看一个例子

从V 0 开始遍历
    遍历分析:V0有两个邻接点V1V2,选择下标最小的V1遍历。接着从V1开始深度遍历,V1只有邻接点V3,也就是没有选的:遍历V3。接着从V3开始遍历,V3只有邻接点V0,而V0已经被遍历过。此时出现了上面提到的情况一,开始回溯V1V1无未被遍历的邻接点,接着回溯V0V0有一个未被遍历的邻接点V2,新的一轮深度遍历从V2开始。V2无邻接点,且无法回溯。此时出现了情况二,检测visited[i],只有V4了。深度遍历完成。看到回溯,应该可以想到需要使用栈。
遍历序列是
V0->V1->V3->V2->V4
从其它顶点出发的深度优先遍历序列是:
V1->V3->V0->V2->V4
V2->V0->V1->V3->V4
V3->V0->V1->V2->V4
V4->V2->V0->V1->V3
以上结果,我们稍后用于测试程序。

结合在图的实现:邻接矩阵中的代码,我们看下在邻接矩阵形式下的图的深度遍历算法:

深度优先代码

/*
深度优先搜索
从vertex开始遍历,visit是遍历顶点的函数指针
*/
void Graph::dfs(int vertex, void (*visit)(int))
{stack<int> s;//visited[i]用于标记顶点i是否被访问过bool *visited = new bool[numV];//count用于统计已遍历过的顶点数int i, count;for (i = 0; i < numV; i++)visited[i] = false;count = 0;while (count < numV){visit(vertex);visited[vertex] = true;s.push(vertex);count++;if (count == numV)break;while (visited[vertex]){for (i = 0; i < numV&& (visited[i] || matrix[vertex][i] == 0 || matrix[vertex][i] == MAXWEIGHT); i++);if (i == numV)  //当前顶点vertex的所有邻接点都已访问完了{if (!s.empty()){s.pop();   //此时vertex正是栈顶,应先出栈if (!s.empty()){vertex = s.top();s.pop();}else  //若栈已空,则需从头开始寻找新的、未访问过的顶点{for (vertex = 0; vertex < numV && visited[vertex]; vertex++);}}else  //若栈已空,则需从头开始寻找新的、未访问过的顶点{for (vertex = 0; vertex < numV && visited[vertex]; vertex++);}}else  //找到新的顶点应更新当前访问的顶点vertexvertex = i;}}delete[]visited;
}
其它代码前面已经见过,就不给出了,下面看下图的广度遍历。深度遍历和广度遍历的测试,稍后一并给出。

广度优先

    广度优先遍历也叫广度优先搜索(Breadth First Search)。它的遍历规则:
  1. 先访问完当前顶点的所有邻接点。(应该看得出广度的意思)
  2. 先访问顶点的邻接点先于后访问顶点的邻接点被访问。
    具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,且顶点V0的邻接点下表从小到大有ViVj...Vk。按规则1,接着应遍历ViVjVk。再按规则2,接下来应遍历Vi的所有邻接点,之后是Vj的所有邻接点,...,最后是Vk的所有邻接点。接下来就是递归的过程...
在广度遍历的过程中,会出现图不连通的情况,此时也需按上述情况二来进行:测试visited[i]...。 在上述过程中,可以看出需要用到队列。

举个例子,还是同样一幅图:

从V 0 开始遍历
    遍历分析:V0有两个邻接点V1V2,于是按序遍历V1V2V1先于V2被访问,于是V1的邻接点应先于V2的邻接点被访问,那就是接着访问V3V2无邻接点,只能看V3的邻接点了,而V0已被访问过了。此时需检测visited[i],只有V4了。广度遍历完毕。
遍历序列是
V0->V1->V2->V3->V4
从其它顶点出发的广度优先遍历序列是
V1->V3->V0->V 2 -> V 4
V2->V0->V1->V3->V4
V3->V0->V1->V2->V4
V4->V2->V0->V1->V3
以上结果,我们同样用于测试程序。

在邻接矩阵下,图的广度遍历算法

广度优先代码

/*
广度优先搜索
从vertex开始遍历,visit是遍历顶点的函数指针
*/
void Graph::bfs(int vertex, void(*visit)(int))
{//使用队列queue<int> q;//visited[i]用于标记顶点i是否被访问过bool *visited = new bool[numV];//count用于统计已遍历过的顶点数int i, count;for (i = 0; i < numV; i++)visited[i] = false;q.push(vertex);visit(vertex);visited[vertex] = true;count = 1;while (count < numV){if (!q.empty()){vertex = q.front();q.pop();}else{for (vertex = 0; vertex < numV && visited[vertex]; vertex++);visit(vertex);visited[vertex] = true;count++;if (count == numV)return;q.push(vertex);}//代码走到这里,vertex是已经访问过的顶点for (int i = 0; i < numV; i++){if (!visited[i] && matrix[vertex][i] > 0 && matrix[vertex][i] < MAXWEIGHT){visit(i);visited[i] = true;count ++;if (count == numV)return;q.push(i);}}}delete[]visited;
}

结合两种遍历的代码,我们对同一幅图进行测试,它的主函数是
void visit(int vertex)
{cout << setw(4) << vertex;
}
int main()
{cout << "******图的遍历:深度优先、广度优先***by David***" << endl;bool isDirected, isWeighted;int numV;cout << "建图" << endl;cout << "输入顶点数 ";cin >> numV;cout << "边是否带权值,0(不带) or 1(带) ";cin >> isWeighted;cout << "是否是有向图,0(无向) or 1(有向) ";cin >> isDirected;Graph graph(numV, isWeighted, isDirected);cout << "这是一个";isDirected ? cout << "有向、" : cout << "无向、";isWeighted ? cout << "有权图" << endl : cout << "无权图" << endl;graph.createGraph();cout << "打印邻接矩阵" << endl;graph.printAdjacentMatrix();cout << endl;cout << "深度遍历" << endl;for (int i = 0; i < numV; i++){graph.dfs(i, visit);cout << endl;}cout << endl;cout << "广度遍历" << endl;for (int i = 0; i < numV; i++){graph.bfs(i, visit);cout << endl;}system("pause");return 0;
}

运行



仔细对照测试结果,我们的代码是没有问题的。

完整代码下载:图的遍历:深度优先、广度优先

小结

对于某个图来说,深度优先遍历和广度优先遍历的序列不是唯一的,但当图的存储结构一确定,它的遍历序列就是唯一的。因为当有多个候选点时,我们总是优先选择下标最小的。


转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/38323633

若有所帮助,顶一个哦!

专栏目录:
  • 数据结构与算法目录
  • c指针



http://chatgpt.dhexx.cn/article/adat1whM.shtml

相关文章

深度优先搜索与广度优先搜索

算法是作用于具体数据结构之上的&#xff0c;深度优先搜索算法和广度优先搜索算法都是基于“图”这种数据结构的。这是因为&#xff0c;图这种数据结构的表达能力很强&#xff0c;大部分涉及搜索的场景都可以抽象成“图”。 图上的搜索算法&#xff0c;最直接的理解就是&#…

广度优先搜索和深度优先搜索

文章目录 1. 前言2. 广度优先搜索和深度优先搜索1&#xff09;深度优先搜索2&#xff09;广度优先搜索 3. 深度优先搜索算法框架1&#xff09;二叉树深度优先搜索模板2&#xff09;图深度优先搜索模板3&#xff09;二维矩阵深度优先搜索模板 4. 广度优先搜索算法框架1&#xff…

深度优先和广度优先算法

1、深度优先算法 遍历规则&#xff1a;不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。 最后得出的结果为&#xff1a;ABDECFHG。 Python代码实现的伪代码如下&#xff1a; 2、广度优先算法&#xff1a; 遍历规则&#xff1a; 1&#xff09;先访问完当…

深度优先搜索(DFS)和广度优先搜索(BFS)

代码随想录 深度优先搜索和广度优先搜索&#xff0c;都是图形搜索算法&#xff0c;它两相似&#xff0c;又却不同&#xff0c;在应用上也被用到不同的地方。这里拿一起讨论&#xff0c;方便比较。 先给大家说一下两者大概的区别&#xff1a; 如果搜索是以接近起始状态的程序依次…

算法:深度优先和广度优先(DFS,BFS)

一丶深度优先&#xff08;DFS&#xff09; 深度优先顾名思义: 就是往深的地方优先查找或遍历。 如图二叉树&#xff0c;想遍历树中所有结点可以用中序遍历&#xff0c;前序或后序。如果某一结点还有子结点就会往深处就是往下一结点&#xff0c;一直遍历直到最后一个结点没有子…

【算法】深度优先和广度优先

本文只是总结的相关概念&#xff0c;仅供自己复习&#xff0c;严禁转载&#xff0c;文末附有本文内容涉及的文章链接&#xff0c;请点开链接查看原文&#xff01; &#xff08;一&#xff09;深度优先 深度优先搜索属于图算法的一种&#xff0c;是一个针对图和树的遍历算法&am…

算法:深度优先遍历和广度优先遍历

什么是深度、广度优先遍历 图的遍历是指&#xff0c;从给定图中任意指定的顶点&#xff08;称为初始点&#xff09;出发&#xff0c;按照某种搜索方法沿着图的边访问图中的所有顶点&#xff0c;使每个顶点仅被访问一次&#xff0c;这个过程称为图的遍历。遍历过程中得到的顶点…

ms17010利用失败解决一则

没有反弹得到session并且提示如下&#xff1a; [-] 10.0.131.2:445 - Service failed to start, ERROR_CODE: 216 换了一个payload set payload windows/meterpreter/reverse_tcp set payload windows/x64/meterpreter/bind_tcp 就可以了。 如果遇到Unable to continue with i…

永恒之蓝MS17010复现

MS17010复现 靶机win7&#xff1a;192.168.41.150 攻击kali: 192.168.41.147 扫描 通过auxiliary/scanner/smb/smb_ms17_010模块扫描虚拟机是否存在ms17010漏洞 存在 拿shell 通过exploit/windows/smb/ms17_010_eternalblue 直接exp打&#xff0c;设置好参数和payload,window…

MS17010(永恒之蓝)漏洞利用与复现

MS17010(永恒之蓝)漏洞利用与复现 0X00简介 永恒之蓝是指2017年4月14日晚&#xff0c;黑客团体Shadow Brokers&#xff08;影子经纪人&#xff09;公布一大批网络攻击工具&#xff0c;其中包含“永恒之蓝”工具&#xff0c;“永恒之蓝”利用Windows系统的SMB漏洞可以获取系统…

网安学习记录1 ms17010漏洞

使用nmap对win7进行端口扫描 进行ms17-010漏洞利用

ms17010漏洞复现-2003

先使用Smbtouch模块检测一下是否有漏洞。 然后使用Doublepulsar写一个shellcode到本地。 生成成功后的截图&#xff1a; 再使用EternalRomance植入Doublepulsar后门。 成功的截图&#xff1a; PS:仿佛是由于之前已经上传过bin的缘故&#xff0c;第二次测试的时候失败了。但是不…

微软永恒之蓝ms17010补丁下载-wannacry

勒索病毒爆发:上百国家遭"感染",Windows勒索病毒恐怖蔓延!勒索病毒,掀起了全球上百个国家、数十亿用户对网络安全的恐慌,微软推出的永恒之蓝ms17010补丁下载专为勒索病毒专杀而设计,同时推出的勒索病毒文件恢复软件是大家得力的勒索病毒文件恢复助手!http://www.cata…

永恒之蓝漏洞自查-MS17010漏洞自查与修复

MS17010漏洞时间线回溯 2017-03-12&#xff0c;微软发布MS17-010补丁包2017-03-14&#xff0c;微软发布《MS17-010&#xff1a;Windows SMB 服务器安全更新说明》 https://support.microsoft.com/zh-cn/help/4012598/title2017-04-14&#xff0c;Shadowbroker发布漏洞利用工具…

ms17010复现

关于漏洞的复现干多了就发现&#xff0c;这种菜鸟级别的复现&#xff0c;&#xff0c;真是没有啥实用性&#xff0c;主要就是&#xff0c;自己玩玩&#xff0c;&#xff0c;&#xff0c;唉&#xff0c;&#xff0c; ms17_010,好像跟什么永恒之蓝&#xff0c;勒索病毒有啥关系。…

ms17010漏洞利用(主机漏洞利用)

攻击机&#xff1a;kali ip:192.168.248.129 靶机&#xff1a;win7 x64 enterprise ip: 192.168.248.130 启动msf&#xff0c; Msfconsole search ms 检查msf库里是否存在ms17-010漏洞 Nmap检测靶机开放端口&#xff1a;nmap –sV 192.168.248.130 445端口开放&#xff0c…

永恒之蓝(MS17010)漏洞kali使用MSF进行漏洞复现

永恒之蓝是指2017年4月14日晚&#xff0c;黑客团体Shadow Brokers&#xff08;影子经纪人&#xff09;公布一大批网络攻击工具&#xff0c;其中包含“永恒之蓝”工具&#xff0c;“永恒之蓝”利用Windows系统的SMB漏洞可以获取系统最高权限。 恶意代码会扫描开放445文件共享端…

永恒之蓝-MS17010 CVE-2017-0146

提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、永恒之…

永恒之蓝(ms17010)漏洞利用

目录 1.启动msf 2.检查msf库里是否存在ms17-010漏洞 3.扫描靶机开放端口 4.配置目标ip&#xff0c;本地ip并利用exploit进行攻击 5.查看靶机系统信息、用户身份并对远程主机当前屏幕截图 6.获得shell控制台进cmd操作&#xff0c;添加账户提升权限 7.远程登录靶机 8.输入…

〖EXP〗NSA MS17010永恒之蓝漏洞一键工具

漏洞简介 永恒之蓝是指2017年4月14日晚,黑客团体Shadow Brokers(影子经纪人)公布一大批网络攻击工具,其中包含“永恒之蓝”工具,“永恒之蓝”利用Windows系统的SMB漏洞可以获取系统最高权限。5月12日,不法分子通过改造“永恒之蓝”制作了wannacry勒索病毒,英国、俄罗斯…