数据结构——图的操作算法(三)

一、实验目的及要求

1.熟悉各种图的存储结构(邻接矩阵和邻接表)。
2.掌握图的深度优先和广度优先遍历算法。
3.掌握克鲁斯卡尔算法生成最小生成树的方法。
4.掌握狄克斯特拉算法计算最短路径和最短路径长度的方法。

二、实验内容(或实验原理、实验拓扑)

1.采用克鲁斯卡尔(Kruskal)算法,输出下面无向带权图G的最小生成树。

三、实验设计方案(包括实验步骤、设计思想、算法描述或开发流程等)

(一)在graph.h中创建图的两种存储结构:
1.定义邻接矩阵的定点类型VertexType;
2.定义完整的图邻接矩阵类型MatGraph。
3.定义邻接表类型的边结点类型ArcNode;
4.定义邻接表类型的头结点类型VNode;
5.定义完整的图邻接表类型AdjGraph。
(二)在graph.cpp中完成邻接矩阵和邻接表的基本运算算法:
1. 创建图的邻接矩阵CreateMat(MatGraph &g,int A[MAXV][MAXV],int n,int e);
2. 输出邻接矩阵g DispMat(MatGraph g);
3. 创建图的邻接表CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e);
4. 输出邻接表G DispAdj(AdjGraph *G);
5. 销毁图的邻接表DestroyAdj(AdjGraph *&G)。
(三)在Kruskal.cpp中完成图的基本运算算法:
1. 对E[0…n-1]按递增有序进行直接插入排序InsertSort(Edge E[],int n);
2. 完成克鲁斯卡尔算法Kruskal(MatGraph g);
3.主函数 main()根据问题依次调用基本操作函数并编写通俗易懂的语句输出。

四、实验结果(包括设计效果、测试数据、运行结果等)

运行结果如下:
在这里插入图片描述

五、实验小结(包括收获、心得体会、注意事项、存在问题及解决办法、建议等)

克鲁斯卡尔算法的时间复杂度为O(elog2e),适合于求边稀疏的网的最小生成树。克鲁斯卡尔算法过程如下,首先考虑问题的出发点,为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小。算法思想是将图中所有边按权值递增顺序排列,依次选定取权值较小的边,但要求后面选取的边不能与前面选取的边构成回路,若构成回路,则放弃该条边,再去选后面权值较大的边,n个顶点的图中,选够n-1条边即可。

六、附录(包括作品、流程图、源程序及命令清单等)

graph.h:
//图的两种存储结构
#define INF 32767				//定义∞
#define	MAXV 100				//最大顶点个数
typedef char InfoType;

//以下定义邻接矩阵类型
typedef struct
{	int no;						//顶点编号
	InfoType info;				//顶点其他信息
} VertexType;					//顶点类型
typedef struct
{	int edges[MAXV][MAXV];		//邻接矩阵数组
	int n,e;					//顶点数,边数
	VertexType vexs[MAXV];		//存放顶点信息
} MatGraph;						//完整的图邻接矩阵类型

//以下定义邻接表类型
typedef struct ANode
{	int adjvex;					//该边的邻接点编号
	struct ANode *nextarc;		//指向下一条边的指针
	int weight;					//该边的相关信息,如权值(用整型表示)
} ArcNode;						//边结点类型
typedef struct Vnode
{	InfoType info;				//顶点其他信息
	int count;					//存放顶点入度,仅仅用于拓扑排序
	ArcNode *firstarc;			//指向第一条边
} VNode;						//邻接表头结点类型
typedef struct 
{	VNode adjlist[MAXV];		//邻接表头结点数组
	int n,e;					//图中顶点数n和边数e
} AdjGraph;						//完整的图邻接表类型

graph.cpp:
//图的基本运算算法
#include <stdio.h>
#include <malloc.h>
#include "graph.h"

//邻接矩阵的基本运算算法
void CreateMat(MatGraph &g,int A[MAXV][MAXV],int n,int e) //创建图的邻接矩阵
{
	int i,j;
	g.n=n; g.e=e;
	for (i=0;i<g.n;i++)
		for (j=0;j<g.n;j++)
			g.edges[i][j]=A[i][j];
}
void DispMat(MatGraph g)	//输出邻接矩阵g
{
	int i,j;
	for (i=0;i<g.n;i++)
	{
		for (j=0;j<g.n;j++)
			if (g.edges[i][j]!=INF)
				printf("%4d",g.edges[i][j]);
			else
				printf("%4s","∞");
		printf("\n");
	}
}

//邻接表的基本运算算法
void CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e) //创建图的邻接表
{
	int i,j;
	ArcNode *p;
	G=(AdjGraph *)malloc(sizeof(AdjGraph));
	for (i=0;i<n;i++)								//给邻接表中所有头结点的指针域置初值
		G->adjlist[i].firstarc=NULL;
	for (i=0;i<n;i++)								//检查邻接矩阵中每个元素
		for (j=n-1;j>=0;j--)
			if (A[i][j]!=0 && A[i][j]!=INF)			//存在一条边
			{	p=(ArcNode *)malloc(sizeof(ArcNode));	//创建一个结点p
				p->adjvex=j;
				p->weight=A[i][j];
				p->nextarc=G->adjlist[i].firstarc;	//采用头插法插入结点p
				G->adjlist[i].firstarc=p;
			}
	G->n=n; G->e=n;
}
void DispAdj(AdjGraph *G)	//输出邻接表G
{
	int i;
	ArcNode *p;
	for (i=0;i<G->n;i++)
	{
		p=G->adjlist[i].firstarc;
		printf("%3d: ",i);
		while (p!=NULL)
		{
			printf("%3d[%d]→",p->adjvex,p->weight);
			p=p->nextarc;
		}
		printf("∧\n");
	}
}
void DestroyAdj(AdjGraph *&G)		//销毁图的邻接表
{	int i;
	ArcNode *pre,*p;
	for (i=0;i<G->n;i++)			//扫描所有的单链表
	{	pre=G->adjlist[i].firstarc;	//p指向第i个单链表的首结点
		if (pre!=NULL)
		{	p=pre->nextarc;
			while (p!=NULL)			//释放第i个单链表的所有边结点
			{	free(pre);
				pre=p; p=p->nextarc;
			}
			free(pre);
		}
	}
	free(G);						//释放头结点数组
}
Kruskal.cpp:
//Kruskal算法
#include "graph.cpp"
#define MaxSize 100
typedef struct 
{	
    int u;			//边的起始顶点
    int v;			//边的终止顶点
    int w;			//边的权值
}Edge;

void InsertSort(Edge E[],int n) //对E[0..n-1]按递增有序进行直接插入排序
{
	int i,j;
	Edge temp;
	for (i=1;i<n;i++) 
	{
		temp=E[i];
		j=i-1;				//从右向左在有序区E[0..i-1]中找E[i]的插入位置
		while (j>=0 && temp.w<E[j].w) 
		{
			E[j+1]=E[j];	//将关键字大于E[i].w的记录后移
			j--;
		}
		E[j+1]=temp;		//在j+1处插入E[i] 
	}
}
void Kruskal(MatGraph g)            //Kruskal算法
{
	int i,j,u1,v1,sn1,sn2,k;
	int vset[MAXV];                 //记录每个顶点所在的连通分量编号
	Edge E[MaxSize];				//存放所有边
	k=0;							//E数组的下标从0开始计
	for (i=0;i<g.n;i++)				//由g产生的边集E
		for (j=0;j<=i;j++)
		{
			if (g.edges[i][j]!=0 && g.edges[i][j]!=INF)
			{
				E[k].u=i;E[k].v=j;E[k].w=g.edges[i][j];
				k++;
			}
		}
	InsertSort(E,g.e);				//采用直接插入排序对E数组按权值递增排序
	for (i=0;i<g.n;i++) 			//初始化辅助数组
		vset[i]=i;
	k=1;                 			//k表示当前构造生成树的第几条边,初值为1
	j=0;                 			//E中边的下标,初值为0
	while (k<g.n)       			//生成的边数小于n时循环
	{	
		u1=E[j].u;v1=E[j].v;        //取一条边的头尾顶点
		sn1=vset[u1];
		sn2=vset[v1]; 				//分别得到两个顶点所属的集合编号
		if (sn1!=sn2)     	  		//两顶点属于不同的集合,该边是最小生成树的一条边
		{	
			printf("  (%d,%d):%d\n",u1,v1,E[j].w);
			k++;                    //生成边数增1
			for (i=0;i<g.n;i++)     //两个集合统一编号
				if (vset[i]==sn2)  	//集合编号为sn2的改为sn1
				    vset[i]=sn1;
		}
		j++;   						//扫描下一条边
	}
}
int main()
{
	MatGraph g;
	int A[MAXV][MAXV]={
		{0,5,8,7,INF,3},
		{5,0,4,INF,INF,INF},
		{8,4,0,5,INF,9},
		{7,INF,5,0,5,6},
		{INF,INF,INF,5,0,1},
		{3,INF,9,6,1,0}};
	int n=6, e=10;
	CreateMat(g,A,n,e);			//建立邻接矩阵
	printf("图G的邻接矩阵:\n");
	DispMat(g);					//输出邻接矩阵
	printf("Kruskal算法结果\n");
	Kruskal(g);
	return 1;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值