关于数据结构(十一)

数据结构计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率数据结构往往同高效的检索算法索引技术有关。

目录

图的遍历

1.深度优先搜索

1.深度优先搜索遍历的过程

2.深度优先搜索遍历的算法实现

2.广度优先搜索

1.广度优先搜索遍历的过程

2.广度优先搜索遍历的算法实现

3.小总结


图的遍历

和树的遍历类似,图的遍历也是从图中某一顶点出发,按照某种方法对图中所有顶点进行访问而且仅访问一次。图的遍历算法是求解图的连通性问题、拓扑排序和关键路径等算法的基础。

然而,图的遍历要比树的遍历复杂得多。因为图的任一顶点都可能和其余顶点相邻接,所以在访问了某个顶点之后,可能沿着某条路径搜索之后,又回到该顶点上。例如图中所示的G2,由于图中存在回路,因此在访问了V1、V2、V3、V4之后,沿着边<V4,V1>又可访问到V1。为了避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已访问过的顶点。为此,设一个辅助数组visited[n],其初始值置为“false”或者0,一旦访问了顶点Vi,便置visited[i]为“true”或者1.

根据搜索路径的方向,通常有两条遍历图的路径:深度优先搜索和广度优先搜索。他们对无向图和有向图都适用。

1.深度优先搜索

1.深度优先搜索遍历的过程

深度优先搜索遍历类似于树的先序遍历,是树的先序遍历的推广。

对于一个连通图,深度优先搜索遍历的过程如下。

(1)从图中某个顶点v出发,访问v。

(2)找出刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。

(3)返沪前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点。

(4)重复步骤(2)和步骤(3),直至图中所有顶点都被访问过,搜索结束 。

(1)从顶点V1出发,访问V1.

(2)在访问了顶点V1之后,选择第一个未被访问的邻接点V2,访问V2。以V2为新顶点,重复此步骤,访问V4,V8,V5.在访问了V5之后,由于V5的邻接点都已经被访问,此步骤结束。

(3)搜索从V5回到V8,由于同样的理由,搜索继续回到V4、V2、直至V1,此时由于V1的另一个邻接点未被访问,则搜索又从V1到V3,再继续进行下去。由此,得到的顶点访问序列为:

                                             V1->V2->V4->V8->V5->V3->V6->V7

所有顶点加上标有实箭头的边,构成一棵以V1为根的树,称之为深度优先生成树

2.深度优先搜索遍历的算法实现

接下来我们选用无向图的深度优先搜索遍历来举例说明,代码段选用C++进行讲解,为了更好的迎合教材,我们使用大量的替换符来替换数据类型。以下为头文件及替换符部分。

#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
#define MaxInt 32767   //表示极大值,即∞
#define MVNum 100      //最大顶点数
typedef int VerTexType;//假设顶点的数据类型为整型
typedef int ArcType;   //假设边的权值类型为整型

然后是创建表结构体部分,创建一个顶点表以及邻接矩阵数组,和点数边数。

typedef struct
{
	VerTexType vexs[MVNum];     //顶点表
	ArcType arcs[MVNum][MVNum]; //邻接矩阵
	int vexnum,arcnum;          //图的当前点数和边数
	int visited[MVNum];
}AMGraph;

因为我们在图的输入部分中需要用到查找位置函数,所以提前对其进行定义声明。

Status LocateVex(AMGraph G,VerTexType v) //查询顶点v在图G中的下标位置
{
	for(int i=0;i<G.vexnum;i++){
		if(G.vexs[i]==v){
			return i;
		}
	}
}

紧接着是图的输入部分,由于是无向图的搜索遍历,所以要双向插入,也就是需要插入两次。

Status CreateUDG(AMGraph *G){
	scanf("%d",&G->vexnum);
	scanf("%d",&G->arcnum);
	for(int i=0;i<G->vexnum;i++){
		scanf("%d",&G->vexs[i]);//输入点的信息
	} 
	int v1,v2;
	int m,n;
	for(int j=0;j<G->arcnum;j++){
		scanf("%d",&v1);//输入一条边依附的顶点
		scanf("%d",&v2);	
		int m=LocateVex(*G,v1);
		int n=LocateVex(*G,v2);
		G->arcs[m][n]=1;
		G->arcs[n][m]=1;
	}
		for(int i=0;i<G->vexnum;i++){
			G->visited[i]=0;
		}
	return OK;
}

然后是图的输出部分。简单套用一个for循环对二维数组进行输出。

Status PrintAMGraph(AMGraph G){
	for(int i=0;i<G.vexnum;i++){
		for(int j=0;j<G.vexnum;j++){
			printf("%d ",G.arcs[i][j]);
		}
		printf("\n");
	}
	return OK;
}

再然后就是核心的深度优先遍历算法部分。首先判断是否为空表,然后对顶点进行标记。

void DFSTraverse(AMGraph *G,int v)
{
    int n=G->vexnum;//顶点数目
    if(v<0||v>=n) return ;
    printf("%d ",G->vexs[v]);//输出顶点v
    G->visited[v]=1;//被访问过
    for(int j=0;j<n;j++)
        if(G->visited[j]==0&&G->arcs[v][j]==1)//没被访问过且存在边(v,j)
            DFSTraverse(G,j);
}

 最后为主函数部分。

int main()
{
	AMGraph G;

//调用利用邻接矩阵创建无向图的函数CreateUDG
	CreateUDG(&G);
//调用输出邻接矩阵的函数PrintAMGraph
	PrintAMGraph(G);
	DFSTraverse(&G,0);
	return 0;
}

2.广度优先搜索

1.广度优先搜索遍历的过程

广度优先搜索遍历类似于树的按层次遍历的过程。 

广度优先搜索遍历的过程如下。

(1)从图中某个顶点V出发,访问v。

(2)依次访问V的各个未曾访问过的邻接点。

(3)分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问。重复步骤(3),直至图中所有已被访问的顶点的邻接点都被访问到。

例如,对下图进行广度优先搜索遍历的具体过程如下。

(1)从顶点V1出发,访问V1。

(2)依次访问V1的各个未曾访问过的邻接点V2和V3.

(3)依次访问V2的邻接点V4和V5,以及V3的邻接点V6和V7,最后访问V4的邻接点V8.由于这些顶点的邻接点均已经被访问,并且图中所有顶点都被访问,由此完成了图的遍历。得到的顶点访问序列为:

                                         V1->V2->V3 ->V4->V5->V6->V6->7->V8

所有顶点加上标有实箭头的边,构成一棵以V1为根的树,称为广度优先生成树。

2.广度优先搜索遍历的算法实现

接下来我们选用无向图的广度优先搜索遍历来举例说明,代码段选用C++进行讲解,为了更好的迎合教材,我们使用大量的替换符来替换数据类型。以下为头文件及替换符部分。

#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
#define MaxInt 32767   //表示极大值,即∞
#define MVNum 100      //最大顶点数
typedef int VerTexType;//假设顶点的数据类型为整型
typedef int ArcType;   //假设边的权值类型为整型
#define MAXSIZE 100//循环队列 
typedef int QElemType;

然后是创建表结构体部分,创建一个顶点表以及邻接矩阵数组,和点数边数。

typedef struct {
	VerTexType vexs[MVNum];     //顶点表
	ArcType arcs[MVNum][MVNum]; //邻接矩阵
	int vexnum, arcnum;         //图的当前点数和边数
	int visited[MVNum];
} AMGraph;

由于广度优先搜索遍历需要用到栈,所以需要对栈进行声明以及一系列操作,以下为栈的代码部分,其中包括栈的声明,栈的入队函数,销毁函数,出队函数,获取栈顶元素函数,测量深度函数,以及栈的打印输出函数。

typedef struct {
	QElemType *base;
	int front;
	int rear;
} SqQueue;

Status InitQueue_Sq(SqQueue *Q) {
	Q->base = (QElemType*)malloc(sizeof(QElemType) * MAXSIZE);
	if (!Q->base) {
		exit(OVERFLOW);
	}
	Q->front = Q->rear = 0;
	return OK;
}

void DestroyQueue_Sq(SqQueue *Q) {
	Q->base = 0;
}

//此处定义入队函数EnQueue_Sq
Status EnQueue_Sq(SqQueue *Q, QElemType e) {
	if ((Q->rear + 1) % MAXSIZE == Q->front) {
		return ERROR;
	}
	Q->base[Q->rear] = e;
	Q->rear = (Q->rear + 1) % MAXSIZE;
	return OK;
}

Status GetHead_Sq(SqQueue *Q) {
	if (Q->front == Q->rear) {
		return ERROR;
	}
	return Q->base[Q->front];
}

int DeQueue_Sq(SqQueue *Q) {
	if (Q->front == Q->rear) {
		return ERROR;//队空
	}
	int e = Q->base[Q->front];
	Q->front = (Q->front + 1) % MAXSIZE;
	return e;
}

Status QueueLength_Sq(SqQueue *Q) {
	return (Q->rear - Q->front + MAXSIZE ) % MAXSIZE;
}


void StackQueue_Sq(SqQueue *Q) {
	SqQueue q;
	q = *Q;
	while (q.front != q.rear) {
		printf("%d ", q.base[q.front]);
		q.front++;
	}
}

 然后就是无向图的基本创建函数。

Status LocateVex(AMGraph G, VerTexType v) { //查询顶点v在图G中的下标位置
	for (int i = 0; i < G.vexnum; i++) {
		if (G.vexs[i] == v) {
			return i;
		}
	}
}

//此处定义无向图的创建
Status CreateUDG(AMGraph *G) {
	scanf("%d", &G->vexnum);
	scanf("%d", &G->arcnum);
	for (int i = 0; i < G->vexnum; i++) {
		scanf("%d", &G->vexs[i]); //输入点的信息
	}
	int v1, v2;
	int m, n;
	for (int j = 0; j < G->arcnum; j++) {
		scanf("%d", &v1); //输入一条边依附的顶点
		scanf("%d", &v2);
		int m = LocateVex(*G, v1);
		int n = LocateVex(*G, v2);
		G->arcs[m][n] = 1;
		G->arcs[n][m] = 1;
	}
	for (int i = 0; i < G->vexnum; i++) {
		G->visited[i] = 0;
	}
	return OK;
}

然后是无向图的输出部分其中还包括了,头元素和相邻元素。

//此处定义无向图的邻接矩阵的输出
Status PrintAMGraph(AMGraph G) {
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			printf("%d ", G.arcs[i][j]);
		}
		printf("\n");
	}
	return OK;
}
int FirstNeighbor(AMGraph *G, int v) {
	for (int j = 0; j < G->vexnum; j++) {
		if ( G->visited[j] == 0 &&G->arcs[v][j] == 1) { 
		return j;
		}
	}
	return -1;
}

int NextNeighbor(AMGraph *G, int v,int w)
{
		for (int j = w; j < G->vexnum; j++) {
			if (G->visited[j] == 0 && G->arcs[v][j] == 1) { 
			return j;
			}
		}
		return -1;
}

 然后就是广度优先搜索遍历函数段和输出函数段。

//广度优先遍历
void BFSTraverse(AMGraph *G, int v, SqQueue *Q) {     //从顶点v出发,广度优先遍历图G
	printf("%d ", G->vexs[v]); //输出顶点v
	G->visited[v] = 1; //对v做已访问标记
	EnQueue_Sq(Q, v);//入队
	while (QueueLength_Sq(Q) != 0) {
		int v1;
		v1 = DeQueue_Sq(Q); //顶点v出队列
		for (int w = FirstNeighbor(G, v1); w >= 0; w = NextNeighbor(G, v1, w)) //检测v所有邻接点
			if (G->visited[w]==0) {      //w为v的尚未访问的邻接顶点
				printf("%d ", G->vexs[w]); //访问顶点w
				G->visited[w] = 1;        //对w做已访问标记
				EnQueue_Sq(Q, w);           //顶点w入队列
			}
	}
}



void DFSTraverse(AMGraph *G, int v) {
	int n = G->vexnum; //顶点数目
	if (v < 0 || v >= n) return ;
	printf("%d ", G->vexs[v]); //输出顶点v
	G->visited[v] = 1; //被访问过
	for (int j = 0; j < n; j++) {
		if (G->visited[j] == 0 && G->arcs[v][j] == 1) { //没被访问过且存在边(v,j)
			DFSTraverse(G, j);
		}
	}
}

最后的主函数部分。

int main() {
	AMGraph G;
	SqQueue Q;
	InitQueue_Sq(&Q);
//调用利用邻接矩阵创建无向图的函数CreateUDG
	CreateUDG(&G);
//调用输出邻接矩阵的函数PrintAMGraph
	PrintAMGraph(G);
	BFSTraverse(&G, 0, &Q);
	return 0;
}

3.小总结

本次内容主要了讲解了数据结构中的一些基础知识点,主要内容顺序表的有关知识本篇内容都为数据结构的基本思想,若想更深的理解以及体会,还请大家在日常学习中多多努力,希望大家学有所成。 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Joker700

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值