图的遍历(深度优先遍历)

本文深入讲解了图数据结构的基本概念,包括无向图和有向图的区别,顶点的度、入度和出度的概念,以及邻接顶点的定义。同时,提供了C++代码示例,展示了如何创建图结构,进行深度优先遍历,以及如何实现图的显示和清空操作。

首先来讲一下数据结构中图的基本概念:

什么是图结构:

图数据结构是每个数据元素之间可以任意关联,构成了图结构。正是这种任意关联性,导致图结构中的数据关系的复杂性。

典型的图结构包含两个部分:

顶点(vertex): 图中的数据元素。

边(Edge):图中连接这些顶点的线。

所有的顶点构成一个顶点集合,所有边构成边集合,图结构就是由顶点集合和边集合组成。

 

无向图

每条边都没有方向性,这种图称为无向图。

有向图

每条边都有方向性,称为有向图。

 顶点的度(Degree)

 连接顶点的边的数量称为顶点的度。顶点的度在有向图和无向图中具有不同的意义。

 例如上图无向图的顶点 1 的度为 3 。

 对于有向图则稍微复杂点,根据顶点的方向性,顶点有入度和出度之分:

 入度是以该顶点为端点的入边数量,记为ID(V)例如:ID(0) = 3。

 出度是以该顶点为端点的出边数量,记为OD(V)例如:OD(1) = 3。

 

邻接顶点

邻接顶点是指图结构中一条边的两个顶点。无向图邻接顶点比较简单,在有向图中则意义不同。

有向图的入边邻接顶点:连接该顶点的边中的起始顶点。例如 <V0,V1>,V1是V0的入边邻接顶点。

有向图的出边邻接顶点:连接该顶点的边中的结束顶点。例如:<V1,V2>,V1是V2的出边邻接顶点。

 

图的顶点定义

#include <iostream>
using namespace std;

#define MaxNum      20                   //图的最大顶点数
#define MaxValue    65535                //最大值

typedef struct {
	char Vertex[MaxNum];                 //保存顶点信息
	int  GType;                          //图的类型
	int  VertexNum;                      //顶点的数量
	int  EdgeNum;                        //边的数量
	int  EdgeWeight[MaxNum][MaxNum];     //保存边的权
	int  isTrav[MaxNum];                 //遍历标志
}GraphMatrix;

图的创建

void CreateGraph(GraphMatrix  *GM)               //创建邻接矩阵图
{
	int i, j, k;
	int weight;                        //权
	char EstartV, EendV;               //边的起始顶点

	cout << "输入图中各项顶点信息" << endl;
	for (int i = 0; i < GM->VertexNum;i++)        //输入顶点
	{
		getchar();
		cout << "第" << i + 1 << "个顶点: " << endl;
		cin >> GM->Vertex[i];             //保存到各顶点数组元素中
	}

	cout << "输入构成各边的顶点及权值:\n";
	for (k = 0; k < GM->EdgeNum; k++)
	{
		getchar();
		cout << "第" << k + 1 << "条边";
		cin >> EstartV >> EendV >> weight;
		for (i = 0; EstartV != GM->Vertex[i]; i++);   //在已有顶点中查找始点
		for (j = 0; EendV != GM->Vertex[j]; j++);     //在已有顶点中查找终点
		GM->EdgeWeight[i][j] = weight;                //对应位置保存权值
		if (GM->GType == 0)                           //若是无向图
			GM->EdgeWeight[j][i] = weight;            //在对角位置保存权值
		
	}
}

清空图: 

void ClearGraph(GraphMatrix *GM)
{
	int i, j;
	for (i = 0; i < GM->VertexNum; i++)    //清空矩阵
	{
		for (j = 0;j < GM->VertexNum; j++)
		{
			GM->EdgeWeight[i][j] = MaxValue;
		}
	}
}

显示图

void OutGraph(GraphMatrix *GM)      //输出邻接矩阵
{
	int i, j;
	for (j = 0; j < GM->VertexNum; j++)
	{
		cout <<"\t"<< GM->Vertex[j];          //在第一行输出顶点信息
	}
	cout << endl;
	for (int i = 0; i < GM->VertexNum; i++)
	{
		cout << GM->Vertex[i];
		for (j = 0; j < GM->VertexNum; j++)
		{
			if (GM->EdgeWeight[i][j] == MaxValue)    //若权值为最大值
			{
				cout << "\t Z";       //用Z表示无穷大
			}
			else
			{
				cout <<"\t"<<GM->EdgeWeight[i][j];       //输出边的权值
			}
		}
		cout << endl;
	}
}

遍历图:

1.首先,从数组中isTrav中选择一个未被访问的顶点V,将其标记为1,表示已访问。

2.接着,从Vi的一个未被访问过的邻接顶点出发进行深度优先遍历。

3.重复 步骤2,直至图中所有和Vi路径相通的顶点都被访问过。

4.重复步骤3,直至所有顶点都被访问。

void DeepTraOne(GraphMatrix *GM,int n)
{
	int i;
	GM->isTrav[n] = 1;          //标记改顶点已处理过
	cout << "  " << GM->Vertex[n];        //输出结点数据

	//添加处理节点的操作
	for (i = 0; i < GM->VertexNum; i++)
	{
		if (GM->EdgeWeight[n][i] != MaxValue && !GM->isTrav[n])
		{
			DeepTraOne(GM, i);            //递归进行遍历
		}
	}
}

//深度优先遍历
void DeepTraGraph(GraphMatrix *GM)
{
	int i;
	for (i = 0; i < GM->VertexNum; i++)        //清除各顶点遍历标志
	{
		GM->isTrav[i] = 0;
	}
	cout << "深度优先遍历节点: ";
	for (int i = 0; i < GM->VertexNum; i++)
	{
		if (!GM->isTrav[i])           //若该顶点未遍历
		{
			DeepTraOne(GM, i);        //调用函数进行遍历
		}
	}
	cout << endl;
}

main函数:

int main()
{
	GraphMatrix GM;
	cout << "输入生成图的类型: ";
	cin >> GM.GType;
	cout << "输入图的顶点数量: ";
	cin >> GM.VertexNum;
	cout << "输入图的边的数量: ";
	cin >> GM.EdgeNum;
	ClearGraph(&GM);
	CreateGraph(&GM);
	cout << "该图的邻接矩阵数据如下: " << endl;
	OutGraph(&GM);
	DeepTraGraph(&GM);

	system("pause");
	return 0;
}

测试输出:

2. 系统设计 1.用到的抽象数据类型的定义 的抽象数据类型定义: ADT Graph{ 数据对象V:V是具有相同特性的数据元素的集合,称为顶点集 数据关系R: R={VR} VR={<v,w>|v,w∈V且P(v,w),<v,w>表示从v到w的弧, 谓词P(v,w)定义了弧<v,w>的意义或信息} 基本操作P: CreatGraph(&G,V,VR) 初始条件:V是的顶点集,VR是中弧的集合 操作结果:按V和VR的定义构造G DestroyGraph(&G) 初始条件:G存在 操作结果:销毁G InsertVex(&G,v) 初始条件:G存在,v和中顶点有相同特征 操作结果:在G中增添新顶点v …… InsertArc(&G,v,w) 初始条件:G存在,v和w是G中两个顶点 操作结果:在G中增添弧<v,w>,若G是无向的则还增添对称弧<w,v> …… DFSTraverse(G,Visit()) 初始条件:G存在,Visit是顶点的应用函数 操作结果:对进行深度优先遍历,在遍历过程中对每个顶点调用函数Visit一次且仅一次。一旦Visit()失败,则操作失败 BFSTraverse(G,Visit()) 初始条件:G存在,Visit是顶点的应用函数 操作结果:对进行广度优先遍历,在遍历过程中对每个顶点调用函数Visit一次且仅一次。一旦Visit()失败,则操作失败 }ADT Graph 栈的抽象数据类型定义: ADT Stack{ 数据对象:D={ai|ai∈ElemSet,i=1,2,…,n,n≥0} 数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,…,n} 约定an端为栈顶,ai端为栈底 基本操作: InitStack(&S) 操作结果:构造一个空栈S DestroyStack(&S) 初始条件:栈S已存在 操作结果:将S清为空栈 StackEmpty(S) 初始条件:栈S已存在 操作结果:若栈S为空栈,则返回TRUE,否则FALSE …… Push(&S,e) 初始条件:栈S已存在 操作结果:插入元素e为新的栈顶元素 Pop(&S,&e) 初始条件:栈S已存在且非空 操作结果:删除S的栈顶元素,并用e返回其值 StackTraverse(S,visit()) 初始条件:栈S已存在且非空 操作结果:从栈底到栈顶依次对S的每个数据元素调用函数visit(),一旦visit()失败,则操作失效 }ADT Stack 队列的抽象数据类型定义: ADT Queue{ 数据对象:D={ai|ai∈ElemSet,i=1,2,…,n,n≥0} 数据关系:Rl={<ai-1,ai>|ai-1,ai∈D,i=2,…,n} 约定其中ai端为队列头,an端为队列尾。 基本操作: InitQueue(&Q) 操作结果:构造一个空队列Q DestroyQueue(&Q) 初始条件:队列Q已存在 操作结果:队列Q被销毁,不再存在 QueueEmpty(Q) 初始条件:队列Q已存在 操作结果:若Q为空队列,则返回TRUE,否则FALSE …… EnQueue(&Q,e) 初始条件:队列Q已存在 操作结果:插入元素e为Q的新的队尾元素 DeQueue(&Q,&e) 初始条件:Q为非空队列 操作结果:删除Q的队头元素,并用e返回其值 }ADT Queue 2.主程序的流程: 调用CreateDN函数创建的邻接表G; 调用PrintDN函数输出邻接表G; 调用DFSTraverse函数深度优先遍历; 调用BFSTraverse函数广度优先遍历
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值