图的遍历(基础)
创始人
2024-03-28 04:02:33
0

一、图的遍历的相关定义

  •  遍历的定义:从已给的连通图中的某一顶点出发,沿着一些边访遍图中的所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算。
  • 遍历的实质:找每个邻接点的过程。
  • 图的特点:图中可能存在回路,且图中任一顶点都有可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。
  • 怎样避免重复访问?                                                                                                                     解决思路:设置辅助数组visited[n],用来标记每个被访问过的顶点。                                        1.初始状态visited[i]为0  ;2.顶点被访问,改visited[i]为1,防止被多次访问 

二、深度优先搜索 

  • 深度优先搜索(Depth-First Search)是指按照深度方向搜索,它类似于树的先根遍历,是树的先根遍历的推广。 
  • 深度优先搜索连通子图的基本思想是:                                                                                      1.从图中某个顶点v0出发,首先访问v0。                                                                                    2.找出刚访问过的顶点vi的第一个未被访问的邻接点, 然后访问该顶点。以该顶点为新顶点,重复本步骤,直到当前的顶点没有未被访问的邻接点为止。                                                      3.返回前一个访问过的且仍有未被访问的邻接点的顶点, 找出并访问该顶点的下一个未被访问的邻接点,然后执行步骤2 
  • 采用递归的形式说明,则深度优先搜索连通子图的基本思想可表示为:                              1. 访问出发点v0。                                                                                                                      2.依次以v0的未被访问的邻接点为出发点,深度优先搜索图,直至图中所有与v0有路径相通的顶点都被访问。 若此时图中还有顶点未被访问,则另选图中一个未被访问的顶点作为起始点,重复上述深度优先搜索过程,直至图中所有顶点均被访问过为止。 
  • 树的深度优先遍历
void PreOrder(TreeNode* R)
{if (R != NULL){visit(R);//访问根结点while (R还有下一个子树T){PreOrder(T);//先根遍历下一棵子树}}
}

基于邻接矩阵的深度优先遍历 

  •  深度优先遍历的伪代码
bool visited[MAXV];//访问标记数组
void DFS(MGraph G, int v);//从第v个顶点开始,深度优先遍历
{visit(v);//访问第v个顶点visited[v] = true;//将第v个顶点设置为已经访问的标记for (int w = FirstNeighbor(G, v); w > 0; w = NextNeighbor(G, v, w)){if (!visited[w])//第w个顶点为第v个顶点尚未访问的邻接点{DFS(G, w);}}
}注意;
int FirstNeighbor(MGraph G, int x);//求图G中顶点x的第一个邻接点,若有则返回顶点号;
//若没有邻接点或图中不存在x,则返回-1
int NextNeighbor(MGraph G, int x, int y);
//假设图G中第y个顶点是第x个顶点的一个邻接点,
//返回除第y个顶点之外,第x个顶点的下一个邻接点的顶点号;若第y个顶点是第x个顶点的最后一个邻接点,则返回-1
  •  深度优先遍历的动画演示

 

  •  基于邻接矩阵存储方式的遍历完整代码
  •  MGraph.h
#pragma once
#include
#include
#include
using namespace std;#define MaxVertexNum 100//顶点数目的最大值
//#define INFINITY  100000//宏定义常量“无穷”
#define MAXV 100typedef int VertexType;//顶点的数据类型
//typedef char VertexType;//顶点的数据类型
typedef int EdgeType;//带权图中边上权值的数据类型
typedef struct
{VertexType vexs[MaxVertexNum];//顶点表(存放顶点)EdgeType edges[MaxVertexNum][MaxVertexNum];//邻接矩阵,边表(存放任意两个顶点之间的距离)int n, e;//图的当前顶点数和边数/弧数
}MGraph;void CreatMat(MGraph& G, int A[][MAXV], int n);//由数组A[n][n]生成邻接矩阵G
//生成图的邻接矩阵方法1
void DisMGraph(MGraph& G);//打印void Init(MGraph& G);//初始化,将所有顶点都初始化为没有访问的顶点
void DFS(MGraph G, int v);//从第v个顶点开始,深度优先遍历
int FirstNeighbor(MGraph G, int x);//求图G中顶点x的第一个邻接点,若有则返回顶点号;
//若没有邻接点或图中不存在x,则返回-1
int NextNeighbor(MGraph G, int x, int y);//假设图G中顶点y是顶点x的一个邻接点,
//返回除y之外顶点x的下一个邻接点的顶点号;若y是x的最后一个邻接点,则返回-1
  •  MGraph1.cpp
#include"MGraph.h"void CreatMat(MGraph& G, int A[][MAXV], int n)//由数组A[n][n]生成邻接矩阵G
{G.n = n;G.e = 0;cout << "请依次输入顶点信息:";for (int i = 1; i <= G.n; i++){cin >> G.vexs[i];}for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){G.edges[i + 1][j + 1] = A[i][j];//i+1,j+1是为了从为了从二维数组[1][1]开始存储if (A[i][j] != 0 && A[i][j] != INFINITY){G.e++;//边数加1}}}
}
void DisMGraph(MGraph& G)//遍历打印
{for (int i = 1; i <= G.n; i++){for (int j = 1; j <= G.n; j++){cout << G.edges[i][j] << " ";}cout << endl;}
}bool visited[MAXV];//访问标记数组
void Init(MGraph& G)//初始化
{for (int i = 1; i <= G.n; i++){visited[i] = false;//标记为访问}
}void DFS(MGraph G, int v)//从第v个顶点(下标也是v)开始,深度优先遍历
{cout << G.vexs[v] << " ";//访问第v个顶点visited[v] = true;//将第v个顶点改为已经访问的标记for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)){if (!visited[w]){DFS(G, w);}}
}int FirstNeighbor(MGraph G, int x)//求图G中第x个顶点的第一个邻接点,若有则返回顶点号;
//若没有邻接点或图中不存在x,则返回-1
{for (int j = 1; j <= G.n; j++){if (G.edges[x][j] == 1)//寻找第x个顶点的第一个邻接点{return j;//返回第x个顶点的第一个邻接点的顶点号}}return -1;
}
int NextNeighbor(MGraph G, int x, int y)//假设图G中第y个顶点是第x个顶点的一个邻接点,
//返回除第y个顶点之外,第x个顶点的下一个邻接点的顶点号;若第y个顶点是第x个顶点的最后一个邻接点,则返回-1
{int i = 1, j = 1;for (int k = y + 1; k <= G.n; k++)//在邻接矩阵第x个顶点所在行//从第y个顶点之后寻找与第x个顶点邻接的顶点{if (G.edges[x][k] == 1){return k;}}return -1;
}
  • Text.cpp
#include"MGraph.h"
int main()
{MGraph G;int A[][MAXV] = { {0,1,0,0,1,0,0,0},{1,0,0,0,0,1,0,0},{0,0,0,1,0,1,1,0},{0,0,1,0,0,0,1,1},{1,0,0,0,0,0,0,0},{0,1,1,0,0,0,1,0}, {0,0,1,1,0,1,0,1},{0,0,0,1,0,0,1,0} };CreatMat(G, A, 8);//方法1cout << "图的邻接矩阵:" << endl;DisMGraph(G);cout << endl;/*cout << G.vexs[4] << endl;cout<
  •  结果

 

  •  非连通图的深度优先遍历
void DFSTraverse(MGraph G)//对图G进行深度优先遍历
{Init(G);//初始化for (int v = 1; v <= G.n; v++){if (!visited[v])//对每个连通分量调用一次DFS{DFS(G, v);}}
}
  • 完整代码
#pragma once
#include
#include
#include
using namespace std;#define MaxVertexNum 100//顶点数目的最大值
//#define INFINITY  100000//宏定义常量“无穷”
#define MAXV 100typedef int VertexType;//顶点的数据类型
//typedef char VertexType;//顶点的数据类型
typedef int EdgeType;//带权图中边上权值的数据类型
typedef struct
{VertexType vexs[MaxVertexNum];//顶点表(存放顶点)EdgeType edges[MaxVertexNum][MaxVertexNum];//邻接矩阵,边表(存放任意两个顶点之间的距离)int n, e;//图的当前顶点数和边数/弧数
}MGraph;void CreatMat(MGraph& G, int A[][MAXV], int n);//由数组A[n][n]生成邻接矩阵G
//生成图的邻接矩阵方法1
void DisMGraph(MGraph& G);//打印void Init(MGraph& G);//初始化,将所有顶点都初始化为没有访问的顶点
void DFS(MGraph G, int v);//从第v个顶点开始,深度优先遍历
int FirstNeighbor(MGraph G, int x);//求图G中顶点x的第一个邻接点,若有则返回顶点号;
//若没有邻接点或图中不存在x,则返回-1
int NextNeighbor(MGraph G, int x, int y);
//假设图G中第y个顶点是第x个顶点的一个邻接点,
//返回除第y个顶点之外,第x个顶点的下一个邻接点的顶点号;若第y个顶点是第x个顶点的最后一个邻接点,则返回-1
void DFSTraverse(MGraph G);//对图G进行深度优先遍历(图G可以是非连通图)

 

#include"MGraph.h"void CreatMat(MGraph& G, int A[][MAXV], int n)//由数组A[n][n]生成邻接矩阵G
{G.n = n;G.e = 0;cout << "请依次输入顶点信息:";for (int i = 1; i <= G.n; i++){cin >> G.vexs[i];}for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){G.edges[i + 1][j + 1] = A[i][j];//i+1,j+1是为了从为了从二维数组[1][1]开始存储if (A[i][j] != 0 && A[i][j] != INFINITY){G.e++;//边数加1}}}
}
void DisMGraph(MGraph& G)//遍历打印
{for (int i = 1; i <= G.n; i++){for (int j = 1; j <= G.n; j++){cout << G.edges[i][j] << " ";}cout << endl;}
}bool visited[MAXV];//访问标记数组
void Init(MGraph& G)//初始化
{for (int i = 1; i <= G.n; i++){visited[i] = false;//标记为访问}
}void DFS(MGraph G, int v)//从第v个顶点(下标也是v)开始,深度优先遍历
{cout << G.vexs[v] << " ";//访问第v个顶点visited[v] = true;//将第v个顶点改为已经访问的标记for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)){if (!visited[w]){DFS(G, w);}}
}int FirstNeighbor(MGraph G, int x)//求图G中第x个顶点的第一个邻接点,若有则返回顶点号;
//若没有邻接点或图中不存在x,则返回-1
{for (int j = 1; j <= G.n; j++){if (G.edges[x][j] == 1)//寻找第x个顶点的第一个邻接点{return j;//返回第x个顶点的第一个邻接点的顶点号}}return -1;
}
int NextNeighbor(MGraph G, int x, int y)//假设图G中第y个顶点是第x个顶点的一个邻接点,
//返回除第y个顶点之外,第x个顶点的下一个邻接点的顶点号;若第y个顶点是第x个顶点的最后一个邻接点,则返回-1
{int i = 1, j = 1;for (int k = y + 1; k <= G.n; k++)//在邻接矩阵第x个顶点所在行//从第y个顶点之后寻找与第x个顶点邻接的顶点{if (G.edges[x][k] == 1){return k;}}return -1;
}void DFSTraverse(MGraph G)//对图G进行深度优先遍历
{Init(G);//初始化for (int v = 1; v <= G.n; v++){if (!visited[v])//对每个连通分量调用一次DFS{DFS(G, v);}}
}
  • 结果 

 基于邻接表的深度优先遍历

 

  • 邻接表的深度优先遍历的伪代码 
int visited[MAXV] = { 0 };
void DFSA(AGraph* G, int i)//邻接表的深度优先算法,i表示从第i个顶点开始深度优先遍历
{ArcNode* p;visited[i] = 1;cout << G->adjlist[i].data<<" ";//访问顶点vip = G->adjlist[i].firstarc;//取vi边表的头指针while (p)//寻找vi的邻接点vj{if (!visited[p->adjvex])//若vj尚且未被访问,则以vj为出发点向纵深搜索{DFSA(G, p->adjvex);}p = p->nextarc;//找vi的下一个邻接点}
}
  •  完整代码
  • AGraph.h 
#pragma once
#include
#include
#include
using namespace std;//输入流的头文件
#include            
#include     
#include 
#define txtRows       8 //txt行数
#define txtCols       8 //txt列数typedef int ElemType;
typedef int InfoType;
#define MAXV 100
#define INF 10000
typedef struct ANode//边结点类型
{int adjvex;//该边的终点位置struct ANode* nextarc;//指向一下条边的指针InfoType info;//该边的相关信息,如带权图可存放权重
}ArcNode;typedef struct Vode//表头结点的类型
{ElemType data;//顶点信息ArcNode* firstarc;//指向第一条边
}VNode;typedef VNode AdjList[MAXV]; //AdjList是邻接表类型
typedef struct
{AdjList adjlist;//邻接表(表头结点组成一个数组)int n, e;//定义顶点数和边数
}AGraph;//图的领接表类型void CreatAdj(AGraph*& G, int A[][MAXV], int n);//由数组A[n][n]生成邻接矩阵G
//生成图的邻接表
void DisAdj(AGraph* G);//打印
void DFSA(AGraph* G, int i);//邻接表的深度优先算法,i表示从第i个顶点开始深度优先遍历
  •  AGraph1.cpp
#include"AGraph.h"void CreatAdj(AGraph*& G, int A[][MAXV], int n)//由数组A[n][n]生成邻接表G
{G = (AGraph*)malloc(sizeof(AGraph));//分配邻接表空间G->n = n;G->e = 0;cout << "请依次输入顶点信息:";for (int i = 1; i <= n; i++)//邻接表的所有表头结点的指针域都设置为空{cin >> G->adjlist[i].data;G->adjlist[i].firstarc = NULL;}ArcNode* p = NULL;for (int i = 0; i < n; i++){for (int j = n - 1; j >= 0; j--){if (A[i][j] != 0 && A[i][j] != INF)//存在一条边{p = (ArcNode*)malloc(sizeof(ArcNode));//创建一个边结点pp->adjvex = j+1;//该边的终点p->info = A[i][j];//该边的权重p->nextarc = G->adjlist[i+1].firstarc;//将新边结点p用头插法插入到顶点Vi的边表头部G->adjlist[i+1].firstarc = p;G->e++;//对于无向图,边数需要除以2}}}
}void DisAdj(AGraph* G)//打印
{ArcNode* p;for (int i = 1; i <= G->n; i++){cout << "[" << i << "]" << G->adjlist[i].data;p = G->adjlist[i].firstarc;//找到顶点i的第一个邻接点while (p != NULL){cout << "->";cout << p->adjvex;p = p->nextarc;//找到下一个邻接点}cout << endl;}
}int visited[MAXV] = { 0 };
void DFSA(AGraph* G, int i)//邻接表的深度优先算法,i表示从第i个顶点开始深度优先遍历
{ArcNode* p;visited[i] = 1;cout << G->adjlist[i].data<<" ";//访问顶点vip = G->adjlist[i].firstarc;//取vi边表的头指针while (p)//寻找vi的邻接点vj{if (!visited[p->adjvex])//若vj尚且未被访问,则以vj为出发点向纵深搜索{DFSA(G, p->adjvex);}p = p->nextarc;//找vi的下一个邻接点}
}
  •  Text.cpp
#include"AGraph.h"
int main()
{int A[MAXV][MAXV];FILE* fp;errno_t err = fopen_s(&fp, "C:\\Users\\86173\\Desktop\\Data1.txt", "r");//注意:Data.txt的属性中显示的是C:\Users\86173\Desktop//需要将单斜杠变双斜杠,双斜杠变四斜杠if (fp == NULL){cout << "文件读取错误!";return -1;}for (int i = 0; i < txtRows; i++){for (int j = 0; j < txtCols; j++){fscanf_s(fp, "%d", &A[i][j]);/*每次读取一个数,fscanf_s函数遇到空格或者换行结束*/}fscanf_s(fp, "\n");}fclose(fp);AGraph* G;CreatAdj(G, A, 8);cout << "邻接表形式:" << endl;DisAdj(G);cout << endl<<"邻接表的深度遍历序列为:";DFSA(G, 2);return 0;
}
  •  结果

 

  •  Data1.txt数据
0 1 0 0 1 0 0 0
1 0 0 0 0 1 0 0
0 0 0 1 0 1 1 0
0 0 1 0 0 0 1 1
1 0 0 0 0 0 0 0
0 1 1 0 0 0 1 0
0 0 1 1 0 1 0 1
0 0 0 1 0 0 1 0

 

相关内容

热门资讯

不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
APK正在安装,但应用程序列表... 这个问题可能是由于以下原因导致的:应用程序安装的APK文件可能存在问题。设备上已经存在同名的应用程序...
安卓文字转语音tts没有声音 安卓文字转语音TTS没有声音的问题在应用中比较常见,通常是由于一些设置或者代码逻辑问题导致的。本文将...
报告实验.pdfbase.tt... 这个错误通常是由于找不到字体文件或者文件路径不正确导致的。以下是一些解决方法:确认字体文件是否存在:...