图(graph)的遍历----深度优先(DFS)遍历

目录

前言

深度优先遍历(DFS)

1.基本概念

 2.算法思想

3.二叉树的深度优先遍历(例子) 

图的深度优先遍历

1.图(graph)邻接矩阵的深度优先遍历

思路分析

代码实现

2.图(graph)邻接表的深度优先遍历

思路分析

代码实现

递归代码

非递归代码


前言

        在前面学习过二叉树的时候我们就已经接触到深度优先搜索和广度优先搜索,二叉树的前序遍历和后序遍历都属于深度优先遍历的一种,但是对于二叉树这种有规律的数据结很容易理解,但是如果是对于图这种没有规律的数据结构又该如何去实现深度优先和广度优先遍历呢?下面就一起来看看吧!

深度优先遍历(DFS)

1.基本概念

        深度优先搜索是用来遍历或搜索树和图数据结构的算法,它是可以从任意跟节点开始,选择一条路径走到底,并通过回溯来访问所有节点的算法。简单来说就是通过选择一条道路走到无路可走的时候回退到上一个岔路口,并标记这条路已走过,选择另外一条道路继续走,直到走遍每一条路。(一句话概括:一路走到黑,黑了就回头)

 2.算法思想

Dfs思修基于递归思想,通过递归的形式来缩小问题规模,把一件事分割成若干个相同的小事,逐步完成。

深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义,深度优先,则是以深度为准则,先一条路走到底,直到达到目标。这里称之为递归下去。

否则既没有达到目标又无路可走了,那么则退回到上一步的状态,走其他路。这便是回溯上来。

3.二叉树的深度优先遍历(例子) 

为了大家更好的了解深度优先遍历的算法,我下面举一个二叉树深度优先遍历的例子,如下图所示的一个二叉树,对其进行前序遍历(深度优先遍历) 

结果为: A B D H I E J C F K G  

前序遍历就是从根结点出发,一直向左子节点走,直到左子节点不存在然后返回到上一个节点走这个节点的右子节点,然后一直往右子节点走,同样的也是走不通为止就返回。很显然这种一路走到黑,黑了就回头的方式,就是深度优先遍历的过程。

图的深度优先遍历

        对于二叉树的深度优先遍历大家都很好理解,但是如果对于图的话,那怎么去进行深度优先遍历呢?

如图所示,拿到这么一个图我们从V1开始对其进行深度优先遍历,那么每一次遇到分叉点走不同的路径遍历到的结果是不一样的,所以对于一个图的深度优先变量结果可以为多种。

深度优先遍历算法步骤

  1. 访问初始结点v,并标记结点v为已访问。
  2. 查找结点v的第一个邻接结点w。
  3. 若w存在,则继续执行4,如果w不存在,则回到第1步,将从v的下一个结点继续。
  4. 若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤123)
  5. 查找结点v的w邻接结点的下一个邻接结点,转到步骤3。

在此之前我们学习了图的两种储存方式,分别是邻接矩阵和邻接表,那么这两种存储方式进行深度优先遍历结果会有什么不同呢?我们接着看。 

1.图(graph)邻接矩阵的深度优先遍历

思路分析

如上图所示,从2位置开始进行深度优先遍历,由于图可能是具有环形结构的,为了避免进入到环内的死循环,这里我们需要用到一个辅助数组visited来标记某一个顶点是否遍历过,如果遍历过的话,那么就不走这个方向,如果全部的方向都被标记遍历过,那就返回到上一个位置,换一个方向去遍历。(递归算法)

        对于visited数组,初始化为0,表示未访问过,如果访问了的话那么就设置为1 

最后整个图遍历完成之后,visited数组里面的数组全都为1 ,也就是说这个图已经变量完成了

代码实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Maxint 32767
#define Maxnum 100//最大顶点数//数据类型
typedef struct d { char id[10];//……
}
ElemType;//图的邻接数组
typedef struct graph {ElemType vexs[Maxnum];//图数据int matrix[Maxnum][Maxnum];//二维数组矩阵int vexnum;//点数int arcnum;//边数
}Graph;//节点id查找下标
int Locate_vex(Graph G, char* id) {for (int i = 0; i < G.vexnum; i++)if (strcmp(G.vexs[i].id,id)==0)return i;return -1;
}
//构造邻接矩阵(无向图,对称矩阵)(有向图)赋权图
void Create_graph(Graph* G) {printf("请输入顶点个数和边的个数:\n");scanf("%d %d", &G->vexnum, &G->arcnum);//输入点数边数printf("请输入顶点数据:\n");for (int i = 0; i < G->vexnum; i++) {scanf("%s", G->vexs[i].id);}for (int x = 0; x < G->vexnum; x++) {for (int y = 0; y < G->vexnum; y++) {if (x == y)G->matrix[x][y] = 0;//对角线初始化为0elseG->matrix[x][y] = Maxint;//其他初始化为Maxint}}printf("请输入边相关数据:\n");for (int k = 0; k < G->arcnum; k++) {char a[10], b[10];int w;scanf("%s %s %d", a, b, &w);//a->bint i = Locate_vex(*G, a);int j = Locate_vex(*G, b);//矩阵赋值G->matrix[i][j] = w;G->matrix[j][i] = w;//删掉这个,表示有向图}
}//输出矩阵
void print_matrix(Graph G) {printf("矩阵为:\n");for (int i = 0; i < G.arcnum; i++) {for (int j = 0; j < G.arcnum; j++)printf("%-5d ", G.matrix[i][j]);printf("\n");}printf("图的顶点个数和边数:%d,%d\n", G.vexnum, G.arcnum);
}//访问输出
void visit(Graph G,int loca) {printf("%s ", G.vexs[loca].id);
}
//深度优先遍历DFS
void DFS(Graph G, char* begin,int* visited) {int loca = Locate_vex(G, begin);visit(G, loca);visited[loca] = 1;//访问完后,visited对应的下标标记为1for (int i = 0; i < G.vexnum; i++) {//当前顶点对其他顶点进行遍历,如果有通路就执行访问操作if (G.matrix[loca][i] != 0&& G.matrix[loca][i]!=Maxint )if(!visited[i])//如果visited为0的话(下一个顶点未访问过),那么就进入到下一个顶点继续访问DFS(G, G.vexs[i].id , visited);}return;
}int main() {Graph G;Create_graph(&G);print_matrix(G);int* visited = (int*)malloc(sizeof(int) * G.vexnum);memset(visited, 0, sizeof(int)*G.vexnum);//初始化为0printf("DFS:\n");DFS(G, "B" , visited);
}

输出结果:

2.图(graph)邻接表的深度优先遍历

思路分析

前面我们学习过了邻接表是由两种节点组成的,分别是头结点和边节点,头结点是用来储存数据的,而变节点是用来储存于当前节点有通路的节点的引索。如图所示

对于邻接表的话,那怎么去书写代码呢?同样的我们还是需要一个visited数组来标记已经访问过的节点,然后对下一个节点进行判断是否访问过,如果没有访问过那么就进入到下一个节点的递归,如果访问过那就换一个方向继续访问,如果都访问过,那就返回到上一个节点的位置重复以上的操作。下面我提供两种代码的书写方式,分别是递归和非递归来去实现深度优先遍历。

代码实现

创建图代码(邻接表):

#include<stdio.h>
#include<string.h>//数据结构体
typedef struct datatype {char id[10];//字符串编号//………………
}ElemType;
//边节点存储结构
typedef struct arcnode {int index;//指向顶点的位置int weight;//权struct arcnode* nextarc;//指向下一个边节点
}Anode;
//顶点结点存储结构
typedef struct vexnode {ElemType data;Anode* firstarc;
}Vhead;
//图结构
typedef struct {Vhead* vertices;int vexnum;int arcnum;
}Graph;//顶点id查找下标
int Locate_vex(Graph G, char* id) {for (int i = 0; i < G.vexnum; i++)if (strcmp(G.vertices[i].data.id,id)==0)return i;return -1;
}
//创建头节点
void Create_vexhead(Graph *G,int n) {G->vertices = (Vhead*)malloc(sizeof(Vhead) *n);if (!G->vertices) {printf("ERROR\n");exit(-1);}else {for (int i = 0; i < n ; i++) {scanf("%s", G->vertices[i].data.id);G->vertices[i].firstarc = NULL;}}
}
//创建一个边节点
Anode* Create_arcnode(int loca, int w) {Anode* arc = (Anode*)malloc(sizeof(Anode));if (!arc){printf("ERROR\n");exit(-1);}arc->index = loca;arc->nextarc = NULL;arc->weight = w;return arc;
}
//创建邻接表(无向图)(有向图)
void Create_graph(Graph* G) {printf("输入顶点数和边数:\n");scanf("%d %d", &G->vexnum, &G->arcnum);printf("输入顶点数据:\n");Create_vexhead(G, G->vexnum);printf("输入边数据:\n");for (int k = 0; k <G->arcnum; k++) {ElemType a, b;int w;scanf("%s%s%d", a.id, b.id, &w);int i = Locate_vex(*G, a.id);int j = Locate_vex(*G, b.id);//头插法//a->bAnode* p = Create_arcnode(j, w);p->nextarc = G->vertices[i].firstarc;G->vertices[i].firstarc = p;//如果创建有向图的话,直接把下面的代码删掉即可//b->aAnode* q = Create_arcnode(i, w);q->nextarc = G->vertices[j].firstarc;G->vertices[j].firstarc = q;}
}//访问
void visit(Graph G, int index) {printf("%s ", G.vertices[index].data.id);
}//输出图
void print(Graph G) {printf("以下是图的顶点连接关系:\n");for (int i = 0; i < G.vexnum; i++) {printf("%s:", G.vertices[i].data.id);Anode* cur= G.vertices[i].firstarc;while (cur) {visit(G, cur->index);cur = cur->nextarc;}printf("\n");}printf("顶点和边数分别是:%d %d\n", G.vexnum, G.arcnum);
}
递归代码
//深度优先遍历DFS0(递归实现)
void DFS0(Graph G, char* begin_id,int* visited) {int index = Locate_vex(G, begin_id);visit(G, index);visited[index] = 1;//标记这个顶点已经访问过了Anode* p = G.vertices[index].firstarc;//找到这个顶点的下一个位置pwhile (p) {if (visited[p->index] == 0) {//如果没有访问过的话,就进入到下一个递归DFS0(G, G.vertices[p->index].data.id, visited);}p = p->nextarc;//退出之后换一个方向访问}return;
}
非递归代码
//深度优先遍历DFS(非递归实现)
void DFS(Graph G,char* begin_id) {//判断是否变量过int* visited = (int*)malloc(sizeof(int) * G.vexnum);memset(visited, 0, sizeof(int) * G.vexnum);//全部初始化为0//记录路径int* path = (int*)malloc(sizeof(int) * G.vexnum);memset(path, -1, sizeof(int) * G.vexnum);int i = -1;//初始化int index = Locate_vex(G, begin_id);int count = 0;//表示已经访问了多少个顶点Anode* p= G.vertices[index].firstarc;//初始化下一个位置指针pwhile (count<G.vexnum) {if (p) {if (visited[index] == 0) {visit(G, index);visited[index] = 1;i++;path[i] = index;//记录当前路径p = G.vertices[index].firstarc;index = p->index;//指向下一个位置count++;}else {p = p->nextarc;index = p->index;}}//回溯过程else {index = path[i - 1];//返回到上一个位置p = G.vertices[index].firstarc->nextarc;//p指针向后移动一位index = p->index;i--;}}
}

测试结果:

int main() {Graph G;Create_graph(&G);print(G);printf("\n递归DFS0深度优先遍历:\n");int* visited = (int*)malloc(sizeof(int) * G.vexnum);memset(visited, 0, sizeof(int) * G.vexnum);DFS0(G, "B", visited);printf("\n非递归DFS深度优先遍历:\n");DFS(G, "B");
}

 以上就是本期的全部内容了,我们下次接着学习图的广度优先遍历,下次见咯!

分享一张壁纸: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/115942.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux内存管理(9):HugePage

当运行内存需求量较大的应用程序时,如果使用长度为4KB的页,将会产生较多的TLB未命中和缺页异常,严重影响应用程序的性能。如果使用长度为2MB甚至更大的巨型页,可以大幅减少TLB未命中和缺页异常的数量,大幅提高应用程序的性能。这才是内核引入巨型页的真正原因。 一个巨页必…

2023年10月23日--10月29日(主攻光追视频教程)

最好每周完成一样&#xff0c;将来每月完成一样&#xff0c;有成就感。也免得周末迷茫。 光锥目前还有56节&#xff0c; 周二到周五每天4小节。周六日每天20小节&#xff0c;应该可以完成。 即&#xff1a; 周二&#xff1a;9.5-9.8 周三&#xff1a;9.9-10.3 周四&#xff1a…

利用dockerfile升级flink的curl

最近Nusses扫出flink镜像有CURL漏洞&#xff0c;才发现要更新到最新版本 8.4.0&#xff0c;笔者当时flink版本为&#xff1a; flink:1.17.1-scala_2.12-java8 官方镜像仓库&#xff1a;https://hub.docker.com/_/flinkapt源 我试了如上2种方法&#xff0c;都不能更新curl到8…

京东数据分析:2023年9月京东洗烘套装品牌销量排行榜!

鲸参谋监测的京东平台9月份洗烘套装市场销售数据已出炉&#xff01; 根据鲸参谋平台的数据显示&#xff0c;今年9月份&#xff0c;京东平台洗烘套装的销量为7100&#xff0c;环比下降约37%&#xff0c;同比增长约87%&#xff1b;销售额为6000万&#xff0c;环比下降约48%&#…

Rust-后端服务调试入坑记

这篇文章收录于Rust 实战专栏。这个专栏中的相关代码来自于我开发的笔记系统。它启动于是2023年的9月14日。相关技术栈目前包括&#xff1a;Rust&#xff0c;Javascript。关注我&#xff0c;我会通过这个项目的开发给大家带来相关实战技术的分享。 如果你关注过我的Rust 实战里…

JWFD开源工作流矩阵引擎测试版本BUG20231022修正代码

public void ParamFileOutputValue(String paramfile) {String s "";String sp "";String ssp "";List<String> list new ArrayList<String>();int p 0;int k 0;//这个地方要修改为整个参数表的最大行数&#xff0c;而不是起始…

Elasticsearch实践:ELK+Kafka+Beats对日志收集平台的实现

可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个全文搜索引擎&#xff0c;它还提供了分布式的多用户能力&#xff0c;实时的分析&#xff0c;以及对复杂搜索语句的处理能力&#xff0c;使其在众多场景下&#xff0c;如企业搜索&#xff0c;日志和事件数据分析等…

后台交互-首页->与后台数据进行交互,wsx的使用

与后台数据进行交互wsx的使用 1.与后台数据进行交互 // index.js // 获取应用实例 const app getApp() const apirequire("../../config/app.js") const utilrequire("../../utils/util.js") Page({data: {imgSrcs:[{"img": "https://cd…

华为OD 数组二叉树(200分)【java】A卷+B卷

华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应往年部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为OD…

算法随想录算法训练营第四十四天|1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划

1143.最长公共子序列 题目&#xff1a;给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情…

ROI的投入产出比是什么?

ROI的投入产出比是什么&#xff1f; 投入产出比&#xff08;Return on Investment, ROI&#xff09;是一种评估投资效益的财务指标&#xff0c;用于衡量投资带来的回报与投入成本之间的关系。它的计算公式如下&#xff1a; 投资收益&#xff1a;指的是投资带来的净收入&#x…

科学指南针iThenticate自助查重系统重磅上线

科学指南针&#xff0c;一直致力于为科研工作者提供高效、专业的学术支持&#xff0c;近日推出了全新的iThenticate自助查重系统。这一系统的上线&#xff0c;旨在为广大科研工作者提供更加便捷、准确的论文查重服务&#xff0c;进一步规范英文使用&#xff0c;提升科研质量。 …

PyTorch 与 TensorFlow:机器学习框架之战

深度学习框架是简化人工神经网络 (ANN) 开发的重要工具&#xff0c;并且其发展非常迅速。其中&#xff0c;TensorFlow 和 PyTorch 脱颖而出&#xff0c;各自在不同的机器学习领域占有一席之地。但如何为特定项目确定理想的工具呢&#xff1f;本综合指南[1]旨在阐明它们的优点和…

Kotlin中的委托、属性委托和延迟加载

委托模式是一种常用的设计模式&#xff0c;用于将某个对象的责任委托给另一个对象来处理。在Kotlin中&#xff0c;委托可以通过关键字by来实现&#xff0c;主要分为类委托和属性委托两种形式。此外&#xff0c;Kotlin还提供了延迟加载的功能&#xff0c;可以在需要时才进行初始…

WebService SOAP1.1 SOAP1.12 HTTP PSOT方式调用

Visual Studio 2022 新建WebService项目 创建之后启动运行 设置默认文档即可 经过上面的创建WebService已经创建完成&#xff0c;添加HelloWorld3方法&#xff0c; [WebMethod] public string HelloWorld3(int a, string b) { //var s a b; return $"Hello World ab{a …

老卫带你学---leetcode刷题(8. 字符串转换整数 (atoi))

8. 字符串转换整数 (atoi) 问题&#xff1a; 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入字符串并丢弃无用的前导空…

如何高效率地阅读论文

▚ 01 Active versus passive reading: how to read scientific papers? &#x1f4e2;小疑则小悟&#xff0c;大疑则大悟&#xff0c;不疑则不悟。 If you read/do research with small questions in mind, you learn small things. If you do so with big questions in…

华为OD 食堂供餐(100分)【java】A卷+B卷

华为OD统一考试A卷B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目&#xff0c;A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载&#xff0c;请点击以下链接进入&#xff…

Unable to connect to the server: x509: certificate is valid for问题解决

文章目录 环境描述问题描述问题原因解决方案额外问题问题描述问题解决方案新问题 环境描述 Kubernetes版本1.15测试客户端centos7 问题描述 将构建于内网网络环境上的kubernetes集群的/etc/kubernetes/admin.conf文件拷贝到外网的一台装有kubernetes客户端的设备上&#xff…

机器学习中的核方法

一、说明 线性模型很棒&#xff0c;因为它们易于理解且易于优化。他们受苦是因为他们只能学习非常简单的决策边界。神经网络可以学习更复杂的决策边界&#xff0c;但失去了线性模型良好的凸性特性。 使线性模型表现出非线性的一种方法是转换输入。例如&#xff0c;通过添加特征…