图(二)——图的遍历
目录
→ 图的遍历
→ 深度优先搜索遍历
↓ 基本思想:
↓ → 递归深度优先搜索遍历
↓ 算法思想:
↓ →非递归深度优先搜索遍历
↓ 算法思想:
→ 广度优先搜索遍历
↓ 基本思想:
→ 算法实现的综合应用:(无向图为例)
↓ 运行结果:
↓ 算法实现:
↓ → 算法实现过程中的注意点及解决方法:
↓ 注意点一:
↓ 解决方法:
↓ 注意点二:
↓ 解决方法:
图的遍历
图的遍历是从图中某个顶点出发,访遍图中其余顶点,且都访问一次的过程。
图与树类似,不同的是图的每个顶点都有可能与其余所有顶点邻接,为避免重复访问同一个顶点,在遍历的过程中应标记该顶点访问过。
思考:
对于图中有回路,对于连通图,如何实现遍历只访问每个顶点一次,也就是遇到回退的情况或图中顶点都已完成访问时如何确保不会再次访问顶点?
设置一个标志数组 Visited【】实现,此数组用于标记所有顶点是否访问过,初始值为0,如果访问过,更新此顶点的标志为1,这样遇到上述情况时,判断顶点的标志数组是否为1即可解决。
图的遍历方法有:深度优先搜索遍历和广度优先搜索遍历,两种遍历方法对无向图,有向图都适用。通过图的遍历可以知道图是否为连通图。
深度优先搜索遍历
深度优先搜索遍历和二叉树的先序遍历类似,尽可能先对纵深方向进行搜索。
基本思想:
先从图的某个顶点 V0 出发,访问顶点,然后依次从V0 各个未访问的邻接点出发深度优先搜索遍历,直到所有和 V0 相通的顶点都被访问到。如果是非连通图,上述操作结束后,需要再选另一个图中未被访问的顶点作为新的出发点,重复上述操作。
深度优先搜索遍历的结果为:A B D F C G E H I 。
一个顶点可能会有多个邻接点,访问次序不同,所以图的遍历的序列不唯一。
对于邻接矩阵存储结构的图,适合稠密图 ,遍历时,需要检查n^2个元素,时间复杂度为O(n^2)。
邻接表存储结构的图,适合稀疏图,找邻接点只需在边表中所有边结点检查一遍,时间为O(e),时间复杂度为O(n+e)。
递归深度优先搜索遍历
采用递归的方式遍历,如果访问到顶点A1,根据判断条件知其没有邻接点(无法深度优先搜索遍历),需要回退到上一个顶点A,继续查找A的其余邻接点A2,,,
算法思想:
从一个顶点v出发
1、访问顶点,标记此顶点访问过
2、从邻接矩阵中找到顶点位置,依次选择其邻接点且并判断是否访问过,进行深度遍历。
遍历伪代码:
void DFS(Graph g,int v0) {visit(vo); //访问顶点visited[v0]=1; //标记访问过顶点w=FirstAdjVex(g,vo); //查找vo的第一个邻接点while(w!=-1) // 邻接点存在{if(!visited[w]) //邻接点未访问过DFS(g,w);w=NextAdjVex(g,vo,w); // vo的下一个邻接点} } void TraverseG(Graph g) {for(v=0;v<g.vexnum;v++) //对标记数组进行初始化为0,vistied[v]=0;for(v=0;v<g.vexnum;v++){if(!visited[v])DFS(G,v);} }
非递归深度优先搜索遍历
二叉树遍历时已经知道,对于递归遍历可以通过栈转化为非递归遍历的形式。
算法思想:
1、栈初始化
2、访问顶点,标记访问过,该顶点入栈
3、当栈不为空时:
1 、取栈顶顶点的位置,存在未被访问的邻接点w
2、访问顶点w,标记访问,w顶点入栈(便于查找w顶点的其他邻接点)然后在栈不为空的条件下重复上述操作。
3、否则,当前顶点出栈,(表明顶点的邻接点都访问过,或该栈顶顶点没有邻接点,出栈,继续寻找现在栈顶顶点的邻接点)。
广度优先搜索遍历
基本思想:
广度优先搜索遍历与二叉树的层次遍历类似,从某个顶点出发开始访问,先访问该顶点的所有邻接点,然后根据其邻接点的访问顺序再访问各自的所有邻接点,直到所有与顶点路径相通的顶点都被访问到。图中若有未被访问到,另选该顶点作为出发点。
广度优先搜索遍历的结果为:A B C D E F G H I 。
算法思想:
根据队列的特点,先进先出,访问顶点时入队,并标记访问过,然后出队,队头元素出队时,依次将其未被访问到的顶点访问并入队,每个顶点都入队一次。
从V0顶点出发
1、队初始化
2、访问V0顶点,标记访问过,并入队
3、当队不为空时:
出队;
将出队的顶点所有未被访问过的顶点访问,标记并入队。
算法实现的综合应用:(无向图为例)
运行结果:
算法实现:
#include<stdio.h> #include<stdlib.h> #define MAXVEX 20 typedef struct {int arcs[MAXVEX][MAXVEX];char vex[MAXVEX];int vexnum;int arcnum; }AdjMatrix; //对图的顶点初始化 AdjMatrix* Init() {AdjMatrix* G = (AdjMatrix*)malloc(sizeof(AdjMatrix));if (G == NULL)printf("申请内存错误");for (int i = 0; i <MAXVEX; i++) //对矩阵初始化为0{for (int j = 0; j < MAXVEX; j++)G->arcs[i][j] = 0;}for (int i = 0; i < MAXVEX; i++)G->vex[i] = 0;G->vexnum = 0;G->arcnum = 0;return G; } //=================================== 邻接矩阵存储结构创建图 ========================= //查找匹配的顶点在矩阵中的位置 int LocateVex(AdjMatrix* G, char v) {int i;for (i = 1; i <= G->vexnum; i++){if (G->vex[i] == v)return i;}return 0; } AdjMatrix* Create() { AdjMatrix* G=Init();int i, j, k;char vex1, vex2;printf("----输入图的顶点数与边数-----\n");scanf("%d%d", &G->vexnum, &G->arcnum);printf("-----依次输入图中%d个顶点------\n", G->vexnum);for (i = 1; i <=G->vexnum; i++){scanf(" %c",&(G->vex[i])); //注意点一:}printf("-----输入图中%d条边-----\n", G->arcnum);for (k = 1; k <= G->arcnum; k++){printf("第%d条边:\n ", k);scanf(" %c", &vex1);scanf(" %c", &vex2);i = LocateVex(G, vex1);j = LocateVex(G, vex2);G->arcs[i][j] = 1;G->arcs[j][i] = 1;}printf("=================》邻接矩阵建立图完成 ================《\n");return G; } //==================================== 邻接矩阵递归深度搜索遍历 ====================== void DFS(AdjMatrix*G, int v) {int i = 0;static int visited[20]; //注意点二:printf("%c ", G->vex[v]);visited[v] = 1;for (i = 1; i <= G->vexnum; i++){if (G->arcs[v][i] != 0 && visited[i] != 1)DFS(G, i);} } //======================================== 栈 ======================================= //定义结点结构 typedef struct Node {char data;struct Node* next; }Slstacktype; //初始化栈头指针 Slstacktype* Initstack() {Slstacktype* top = (Slstacktype*)malloc(sizeof(Slstacktype));top->next = NULL;return top; } //进栈 Slstacktype* Push(Slstacktype* top,char vex) {Slstacktype* p=(Slstacktype*)malloc(sizeof(Slstacktype));if (p == NULL)return NULL;p->data = vex;p->next = top->next;top->next = p;return top; } //出栈 Slstacktype* Pop(Slstacktype* top) {Slstacktype* q;if (top->next == NULL) //判断栈是否为空return NULL;q = top->next;top->next = q->next;free(q);return top; } //=================================== 非递归邻接矩阵存储结构的深度搜索遍历 ============= //取栈顶顶点的位置 int include_stack(AdjMatrix* G, Slstacktype* top) {int i;for (i = 1; i <= G->vexnum; i++){if (G->vex[i] == top->next->data)return i;}return 0; } void DFS_stack(AdjMatrix*G,int v) {int i;Slstacktype* top = Initstack(); //栈的初始化static int visited[20]; //定义静态的标志数组printf("%c ", G->vex[v]);visited[v] = 1;Push(top, G->vex[v]);while (top->next != NULL){v = include_stack(G, top);for (i = 1; i <= G->vexnum; i++){if (G->arcs[v][i] != 0 && visited[i] != 1){printf("%c ", G->vex[i]);visited[i] = 1;Push(top, G->vex[i]);break;}}if (i > G->vexnum) //顶点的邻接点都访问过,或没有邻接点时退栈顶结点{top = Pop(top);}} }//=========================================== 队列 ====================================== typedef struct Node2 {char data;struct Node2* next; }QNode; typedef struct {QNode* front;QNode* rear; }LQNode; //队的初始化 LQNode* Init_Q() {LQNode* p = (LQNode*)malloc(sizeof(LQNode));QNode* q = (QNode*)malloc(sizeof(QNode));p->front = p->rear = q;q->next = NULL;return p; } //入队 void Push_Q(LQNode* p, char vex) {QNode* q = (QNode*)malloc(sizeof(QNode));if (q == NULL)printf("申请空间错误");q->data = vex;q->next = NULL;p->rear->next = q;p->rear = q; } //出队 char Pop_Q(LQNode* p) {char vex;QNode* q;if (p->front == p->rear)return ;q = p->front->next;p->front->next = q->next;vex = q->data; if (p->front->next == NULL)p->rear = p->front;free(q);return vex; } //========================================== 邻接矩阵的广度优先搜素遍历 ==================== int include_Q(AdjMatrix* G, char vex) //取出队顶点在邻接矩阵中的位置 {for (int i = 1; i <= G->vexnum; i++){if (G->vex[i] == vex)return i;}return 0; } void BFS(AdjMatrix* G, int v) {static visited[20];LQNode* p = Init_Q();printf("%c ", G->vex[v]);visited[v] = 1;Push_Q(p, G->vex[v]);while (p->front->next!=NULL){int i;v = include_Q(G,Pop_Q(p));for (i = 1; i <= G->vexnum; i++){if (G->arcs[v][i] != 0 && visited[i] != 1){printf("%c ", G->vex[i]);visited[i] = 1;Push_Q(p, G->vex[i]);}}} } //======================================== 打印邻接矩阵 ============================== void print(AdjMatrix* G) {for (int i = 1; i <= G->vexnum; i++){for (int j = 1; j <= G->vexnum; j++){printf("%d ", G->arcs[i][j]);}printf("\n");} } main() {int v;printf("==============》 邻接矩阵存储结构创建图 《===========\n");AdjMatrix* G = Create(); printf("==============》 打印邻接矩阵 《============\n");print(G);printf("\n");printf("----输入遍历起始位置-----\n");scanf("%d", &v);printf("==============》 递归 深度搜索遍历 《========== \n");DFS(G, v);printf("\n");printf("==============》 非递归 深度搜索遍历 《========== \n");DFS_stack(G, v);printf("\n");printf("==============》 广度优先搜索遍历 《==============\n ");BFS(G, v);printf("\n"); }
算法实现过程中的注意点及解决方法:
注意点一:
再多次用scanf()函数输入值时,输入的是字符类型的值时,需要考虑scanf函数的问题
解决方法:
可以在 %c前面空格用于接收留在缓冲区中的空格、回车等符号
注意点二:
定义的标志数组,需要初始化为0,每次访问过后需要更新为1,然而在递归函数中,每一次调用都会重新初始化数组,然后就会造成死循环的情况(可以试试写写,印象更深)。
解决方法:
可以定义一个大一点的数组,利用静态数组未初始化就会给所有的数组元素赋初值0,就很好的解决这个问题。
静态数组:静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。 静态数组在内存中位于静态存储区,,在运行时这个大小不能改变,在函数执行完以后,系统自动销毁; 如:int a[10]; 虽然c语言规定,只有静态存储的数组才能初始化,但一般的c编译系统都允许对动态存储的数组赋初值。静态存储的数组如果没有初始化,系统自动给所有的数组元素赋0。
图(二)——图的遍历相关推荐
- 三十二、图的创建深度优先遍历(DFS)广度优先遍历(BFS)
一.图的基本介绍 为什么要有图 前面我们学了线性表和树 线性表局限于一个直接前驱和一个直接后继的关系 树也只能有一个直接前驱也就是父节点 当我们需要表示多对多的关系时, 这里我们就用到了图. 图的举例 ...
- Delphi下实现全屏快速找图找色 二、矩阵遍历
二.矩阵遍历 矩阵遍历是一个数据结构方面的问题.假设有一个矩阵Matrix,它共有RowCount行,每行有ColCount列,当利用y表示行数,x表示列数,那么利用Matrix[y,x]就可以访问矩 ...
- 【数据结构-图】1.图的构造和遍历(基本理论+代码)
一.图的基本概念 图: 图G是一个有序二元组(V,E),其中V称为顶集(Vertices Set),E称为边集(Edges set),E与V不相交.它们亦可写成V(G)和E(G).其中,顶集的元素被称 ...
- 图形结构:克隆图,图的遍历的应用,递归和迭代
克隆一张无向图,图中的每个节点包含一个 label (标签)和一个 neighbors (邻接点)列表 . 克隆图时图的遍历的应用,树的遍历,图的遍历是最基本的操作,他们和数组的遍历是一样的,线性结构 ...
- 图:图的邻接矩阵创建、深度优先遍历和广度优先遍历详解
邻接矩阵介绍 直接说,邻接矩阵是图的一种存储结构.那么图是什么呢?图是一种逻辑结构,和线性结构.树形结构.集合结构一样 是一种逻辑结构用来描述数据对象中的数据元素之间的关系.来看下图的定义:图(Gra ...
- 一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)
相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...
- 《数据结构》实验报告六:图的表示与遍历
一.实验目的 1.掌握图的邻接矩阵和邻接表表示 2.掌握图的深度优先和广度优先搜索方法 3.理解图的应用方法 二.实验预习 说明以下概念 1.深度优先搜索遍历: 一种图的遍历方式:从图中任意一个起始 ...
- 数据结构(二十一)——图和图的应用
文章目录 前言 图(graph) 1)图的基本概念及特性 2)图的描述 a. 无权图描述 b. 加权图描述 3)图的类实现 4)图的遍历 a. 广度优先搜索 b. 深度优先搜索 c. 两种搜索算法的比 ...
- 《图论》——图的存储与遍历(Java)
一:图的分类 1:无向图 即两个顶点之间没有明确的指向关系,只有一条边相连,例如,A顶点和B顶点之间可以表示为 <A, B> 也可以表示为<B, A>,如下所示 2:有向图 顶 ...
- 图深度优先、广度优先遍历(java)
一.图的遍历 图的遍历,即是对结点的访问.一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略:(1)深度优先遍历(2)广度优先遍历深度优先遍历基本思想. 二.深度优先遍历 图的深 ...
最新文章
- 手把手教你用Python构建自己的「王二狗」
- LEADTOOLS HTML5/JavaScript 实现客户端图像处理
- jQuery弹出窗口浏览图片
- java实现一个跳转结构程序,Java程序设计基础(第6版)最新章节_鲜征征著_得间小说...
- idea设置JVM运行参数
- 3、C语言面试笔试--控制结构
- springboot日志可视化_spring boot面试问题集锦
- mybatis学习(14):log4j:ERROR Category option 1 not a decimal integer.
- 【华为云技术分享】ARMv8-A存储模型概述(2)
- IOS中的数据存储 简单总结
- iPhone销售额第四财季同比下滑21% 苹果市值蒸发约千亿美元
- JavaScript中的函数表达式
- leetcode刷题日记-喧闹和富有
- DOM、JDOM、DOM4J解析XML
- 【数学建模】2017年B题
- java for 死循环_关于java编程死循环的应用
- python 网格交易源码_文华财经网格交易源码
- outlook qr码在哪里_Outlook与iPhone并存管理通讯录
- Android dex2oat命令参数解释
- python反序数函数_python逆序函数
热门文章
- wegame饥荒一直连接中_wegame饥荒联机版一直正在启动服务器 | 手游网游页游攻略大全...
- php as 竖排和横排,Row(横排) 与 Column(竖排)《 Flutter 移动应用:布局 》
- android与iPhoneX区别,【苹果iPhoneX评测】Android又遭歧视 连刷朋友圈质量都比iOS低-中关村在线...
- 用户信息姓名,手机号及身份证加*号工具类
- COMP-1及COMP-2内部存储解析
- 头歌实验教学平台C语言之函数
- python前景怎么样-Python 未来发展前景怎么样?
- 【原创】驳:三问瑞星:瑞星发现了在实验室中的病毒?
- Redis 配置信息大全
- 淘宝的数据库,主键是如何设计的?