数据结构 c语言 Prim算法寻找最小生成树的邻接表实现

今天的ddl,明显感觉这周的作业轻松了不少qaq
看到我们院的大佬已经都可以去参加最强大脑了,告诉自己一定要更努力。
下一次准备写一篇文章整合一下前端的知识,然后就要开始动手刷题了
加油奥利给!
在这里非常感谢@黛西珀 给我指正了很多问题

Ps:部分内容需要点赞后查看
在这里插入图片描述

Prim算法介绍

1.概括

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。

2.算法简单描述

1).输入:一个加权连通图,其中顶点集合为V,边集合为E;

2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;

3).重复下列操作,直到Vnew = V:

a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;

4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。

上述内容链接.

图解因为课件描述比较奇怪,大家可以去上面链接里看,大佬整理的很好

我遇到的问题

1.Prim算法实现的时候我主要遇到的问题就是再加入新的节点后,因为需要更新权值信息,对于尚未有权值已经加入树中的,和有权值的,需要分开讨论。
2.一开始实现算法的时候,只定义了权值的数组,后来发现无法确定结点之间的父母-孩子关系,我的做法是新定义的一个结构,里面同时储存权值和指向它的结点信息,当然同时定义两个数组应该也是可以的,大家可以尝试一下
3.因为大量计算涉及到权值,最好另写一个获取权值的函数。
4.最后的输出:因为不想(懒得)写树的部分算法,我选择直接输出边以及父母-孩子关系,大家有时间可以直接生成新的树,只要初始化树之后不断使用插入边的算法就可以了。
小声bb我感觉生成树之后也并没有直观多少,还不如我这么输出

代码实现

头函数和宏、结构定义

#include<stdio.h>
#include<stdlib.h>
#define MAX_VER_TEX 20//定点的最大个数 
#define VertexType int 
#define ERROR -1
#define OK 0
#define MAX 1000000
//typedef enum
//{	Digraph,//有向图 
//	Undigraph//无向图 
//}GraphKind;
typedef struct weightlist{
	VertexType vex;//指向这个顶点的顶点
	int weight; 
}WeightList;
typedef struct ArcNode{//弧结点结构 
	int adjvex;//本弧指向的结点的位置
	struct ArcNode *nextarc;//指向下一个弧的指针
	int weight;//权值 
}ArcNode;
typedef struct VNode{//定点结构
	VertexType data; //储存结点的信息 
	ArcNode* firstarc;//指向第一条弧的指针 
}VNode,AdjList[MAX_VER_TEX];
typedef struct{	//图的结构
	AdjList vertexs;//储存顶点的数组
	int vexnum;//顶点个数 
	int arcnum;//边的个数
	//GraphKind kind;//图的种类  
}UnGraph;

无向图操作相关的子函数

int Locate(UnGraph g,VertexType elem)//定位这个结点在数组中的位置
{	int i;
	for(i=0;i<g.vexnum;i++)
	{	if(elem==g.vertexs[i].data)
		return i; 
	}
	return ERROR;//找不到对应元素,返回-1表示不存在 
}
int CreateUnGraph(UnGraph *g)//输入图结构,图的顶点个数,边个数
{	
	int i;
	int a,b,c,pa,pb;
	ArcNode* p,*p2;
	printf("输入图的有关信息,\n输入格式:图顶点个数+空格+边个数\n");
	scanf("%d %d",&(g->vexnum),&(g->arcnum));
	printf("下面请输入第一个顶点信息:");
	for(i=0;i<g->vexnum;i++)
	{	scanf("%d",&g->vertexs[i].data);
		g->vertexs[i].firstarc=NULL;
		if(i!=g->vexnum-1)
		{	printf("请输入下一个结点的信息:");
		}
	}
	printf("下面输入第一条边的信息,\n输入格式:起点的信息+空格+终点的信息+权值\n");
	for(i=0;i<g->arcnum;i++)
	{	scanf("%d %d %d",&a,&b,&c);
		pa=Locate(*g,a);//起点在数组里的位置 
		pb=Locate(*g,b);//终点在数组里的位置
		if(pa<0||pb<0)
		{	printf("这个边并不存在,请重新输入\n");
			i--;
			continue; 
		}
		p=(ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex=pb;
		p->weight=c;
		//下面把这个边信息插到起点的弧信息的第一个 
		p->nextarc=g->vertexs[pa].firstarc;
		g->vertexs[pa].firstarc=p;
		//在另一个结点的弧域中插入 
		p2=(ArcNode*)malloc(sizeof(ArcNode));
		p2->adjvex=pa;
		p2->weight=c;
		//下面把这个边信息插到起点的弧信息的第一个 
		p2->nextarc=g->vertexs[pb].firstarc;
		g->vertexs[pb].firstarc=p2;
		if(i!=g->arcnum-1)
		printf("请输入下一条边的信息:"); 
		
	}
} 
void PrintOutUnGraph(UnGraph g)
{	int i,j=1;
	ArcNode *p;
	printf("\n*---------分割线---------*\n");
	printf("这个图共有%d个顶点%d条边:\n",g.vexnum,g.arcnum); 
	printf("这个图的顶点信息:\n");
	for(i=0;i<g.vexnum;i++)
	{	printf("该图结构的第%d个顶点的信息是%d\n",i+1,g.vertexs[i].data);
	}
	printf("这个图的边信息:\n");
	for(i=0;i<g.vexnum;i++)
	{	p=g.vertexs[i].firstarc;
		while(p)
		{	if(i<p->adjvex)
			{
				printf("第%d条边:(%d ,%d) 权值为:%d\n",j,g.vertexs[i].data,g.vertexs[p->adjvex].data,p->weight);
				j++;
			}
			p=p->nextarc;
		}
	 }
	printf("*---------分割线---------*\n");  
}
int InsertArc(UnGraph *g,int a,int b,int aweight)
{	int pa,pb;
	ArcNode*p,*p2;
		pa=Locate(*g,a);//起点在数组里的位置 
		pb=Locate(*g,b);//终点在数组里的位置
		if(pa<0||pb<0)
		{	printf("这个边并不存在,请重新输入\n");
			return ERROR;
		}
		p=(ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex=pb;
		p->weight=aweight;
		//下面把这个边信息插到起点的弧信息的第一个 
		p->nextarc=g->vertexs[pa].firstarc;
		g->vertexs[pa].firstarc=p;
		//在另一个结点的弧域中插入 
		p2=(ArcNode*)malloc(sizeof(ArcNode));
		p2->adjvex=pa;
		p2->weight=aweight;
		//下面把这个边信息插到起点的弧信息的第一个 
		p2->nextarc=g->vertexs[pb].firstarc;
		g->vertexs[pb].firstarc=p2;
		
		g->arcnum++;//边个数增加 
		return OK;
}
int DeleteArc(UnGraph *g,int a,int b)
{	int pa,pb;
	ArcNode *p,*temp,*p2;
	pa=Locate(*g,a);//起点在数组里的位置 
	pb=Locate(*g,b);//终点在数组里的位置
	if(pa<0||pb<0)
		return ERROR;
	
	p=g->vertexs[pa].firstarc;
	if(p->adjvex==pb)//p为头结点的情况 
		 {	temp=p;
		 	
		 	g->vertexs[pa].firstarc=p->nextarc;
		 	free(temp);
		 	
		 }
	if(p->nextarc->adjvex==pb)
	{	temp=p->nextarc;
		p->nextarc=temp->nextarc;	
		free(temp);
	}
	//从另一个结点删除 
		p2=g->vertexs[pb].firstarc;
	if(p2->adjvex==pa)//p为头结点的情况 
		 {	temp=p2;
		 	free(temp);
		 	g->vertexs[pb].firstarc=p2->nextarc;
		 }
	if(p2->nextarc->adjvex==pa)
	{	temp=p2->nextarc;
		p2->nextarc=temp->nextarc;	
		free(temp);
	}
	g->arcnum--;//边个数减一 
	return OK;
		 
 } 
//插入一个顶点,返回顶点在图数组里的位置,如果该顶点已经存在,或者数组已满返回-1; 
int InsertVex(UnGraph *g,int adata)
{	int i;
	if(g->vexnum==MAX_VER_TEX)
	return ERROR;
	for(i=0;i<g->vexnum;i++)
	{	if(adata==g->vertexs[i].data)
		return ERROR;
	}
	g->vertexs[g->vexnum].data=adata;
	g->vertexs[g->vexnum].firstarc=NULL;
	g->vexnum++;//顶点个数增加 
	return g->vexnum-1;
}
int Delete(ArcNode *p)//删除顶点的辅助函数:递归调用删除弧结点内容 
{	if(p)
	{
		Delete(p->nextarc);
		free(p);
		return OK;
	}
	else 
		return NULL;
}
int DeleteVex(UnGraph *g,VertexType adata)
{	int qq=0;
	ArcNode *p,*del,*pre;
	int pdata=Locate(*g,adata);//定位结点位置 
	if(pdata<0)//结点不存在,返回错误信息 
	return ERROR;
	//Delete(g->vertexs[pdata].firstarc);//删除这个结点储存的弧信息
	p=g->vertexs[pdata].firstarc;
	while(p)
	{	g->arcnum--;
		p=p->nextarc;
	 } 
	int i;
	for(i=pdata;i<g->vexnum-1;i++)//数组内容移位 
	{	g->vertexs[i].data=g->vertexs[i+1].data;
		g->vertexs[i].firstarc=g->vertexs[i+1].firstarc;//顶点信息和第一条弧的指针都移位 
	}
	g->vertexs[g->vexnum-1].data=-1;
	g->vertexs[g->vexnum-1].firstarc=NULL;
	g->vexnum--;//顶点个数减1 
	for(i=0;i<g->vexnum;i++)
	{	p=g->vertexs[i].firstarc;
		while(p)
		{	if(p->adjvex==pdata)
			{	
				if(p==g->vertexs[i].firstarc)
				{	del=p;
					p=p->nextarc;
					g->vertexs[i].firstarc=p;
					pre=NULL;
					free(del);
					g->arcnum--;
					break;
				}
				else
				{	del=p;
					p=p->nextarc;
					pre->nextarc=p;
					free(del);
					g->arcnum--;
					break;
				}
			}
			else if(p->adjvex>pdata)
			{	p->adjvex--;
			}
			pre=p;
			p=p->nextarc;
		}
		
	}
	return OK; 
}

Prim算法相关函数

int GetWeight(UnGraph g,VertexType a,VertexType b )//获取权值 
{	int pa,pb;
	pa=Locate(g,a);
	pb=Locate(g,b);
	ArcNode* p;
	p=g.vertexs[pa].firstarc;
	while(p!=NULL)
	{	if(p->adjvex==pb)
		return p->weight;
		else
		p=p->nextarc;
	}
	return -1;
}
void Prim(UnGraph g,VertexType root)
{	
	int i,j,min=MAX,minnum;
	WeightList weightlist[g.vexnum];//储存最小权值信息,-1表示没有,0表示已加入 
	int nowvex;//记录当前插入的哪个元素
	nowvex=root;
	for(i=0;i<g.vexnum;i++)
	{	weightlist[i].weight=GetWeight(g,nowvex,g.vertexs[i].data);
		weightlist[i].vex=nowvex;
	 } //初始化 
	  weightlist[Locate(g,nowvex)].weight=0;
	for(j=1;j<=g.vexnum-1;j++) 
	{	min=MAX;
			for(i=0;i<g.vexnum;i++)
		{
			if(weightlist[i].weight<=min&&weightlist[i].weight>0)
			{	
				minnum=i;//最小权值所在坐标 
				min=weightlist[i].weight; 
			}	
		}
		//此时找到最小值位置,和当前新加入的点
		printf("最小生成树上的一条边为(%d ——> %d),权值为%d\n",weightlist[minnum].vex,g.vertexs[minnum].data,weightlist[minnum].weight); 
		nowvex=g.vertexs[minnum].data;
		weightlist[minnum].weight=0;
		for(i=0;i<g.vexnum;i++)
		{	if((weightlist[i].weight!=0&&GetWeight(g,nowvex,g.vertexs[i].data)!=-1)&&(weightlist[i].weight==-1||GetWeight(g,nowvex,g.vertexs[i].data)<weightlist[i].weight))
			{	weightlist[i].weight=GetWeight(g,nowvex,g.vertexs[i].data);
				weightlist[i].vex=nowvex;
			}
		}
	}
}

主程序和结果检验

int main()
{	UnGraph test;
	CreateUnGraph(&test);
	PrintOutUnGraph(test);
	Prim(test,3);
}

结果的检验我使用了作业之前的题
在这里插入图片描述输入:

  5 7
  1
  2
  3 
  4
  5 
  1 2 10
  2 3 50
  3 4 20
  4 5 60
  5 1 100
  3 1 30
  3 5 10

输出:
在这里插入图片描述

  • 39
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值