(C/C++)-最小生成树算法(PrimKruskal)和单源最短路径算法(Dijkstra)

news/2024/7/15 20:13:18 标签: 数据结构, 算法, 图论

(C/C++)-最小生成树算法(Prim&Kruskal)和单源最短路径算法(Dijkstra)

1、什么是最小生成树

对于一个带权连通无向图G=(V,E),图G的不同的生成树,其所对应的生成树的权值可能不同。设R是G所有生成树的集合,T是R中权值最小的那棵生成树,则称T为G的最小生成树

最小生成树的性质

  • 最小生成树可能不唯一,但其对应的边的权值之和总是唯一的,且是最小的;
  • 最小生成树的边数为顶点数-1;

2、Prim算法的实现(选点)

图G=(V,E),其中V为所有结点的集合,E为图G的边的集合
用辅助集合s,来描述被选中的顶点

思路:

  • 首先将第一个顶点放入集合s中,即s={1};
  • 做如下贪心选择,当s是v的真子集时,选取i属于s,j属于V-S,且c[i][j]是最小的边,并将顶点j加入到集合s中;
  • 当S=V时选取结束,此时就生成了一棵最小生成树;

如图所示,描述了Prim算法选取顶点的过程:
在这里插入图片描述
具体实现过程:

void prim(int c[N][N], int n)
{
	int lowcost[N];//存储非集合s顶点到集合s中顶点最小边的权值
	int closest[N];//存储非集合s顶点到集合s中权值最小边的顶点
	int s[N];

	s[1] = 1;
	printf("1 ");

	//lowcost[]和closest[]的初始化
	for (int i = 2; i < N; i++)
	{
		closest[i] = 1;
		lowcost[i] = c[1][i];
		s[i] = 0;
	}

	//每次循环箱集合s中添加一个顶点
	for (int i = 2; i < N; i++)
	{
		int min = maxint;
		int temp = 1;//用来记录选择的顶点

		//做贪心选择,选择非集合s中顶点到s中顶点最小的权值边的顶点
		for (int j = 2; j < N; j++)
			if (!s[j] && min > lowcost[j])
			{
				//更新值,并记录
				min = lowcost[j];
				temp = j;
			}

		s[temp] = 1;//顶点加入到集合s
		printf("%d ", temp);

		//更新lowcost[]和closest[]
		for(int j=2;j<N;j++)
			if (!s[j] && lowcost[j] > c[j][temp])
			{
				lowcost[j] = c[j][temp];
				closest[j] = temp;
			}
	}
}

在main()函数中测试一下Prim算法

int main()
{
	int c[N][N] = {//0号下标不用
		{maxint,maxint,maxint,maxint,maxint,maxint,maxint},//0
		{maxint,maxint,6,1,5,maxint,maxint},//1
		{maxint,6,maxint,5,maxint,3,maxint},//2
		{maxint,1,5,maxint,5,6,4},//3
		{maxint,5,maxint,5,maxint,maxint,2},//4
		{maxint,maxint,3,6,maxint,maxint,6},//5
		{maxint,maxint,maxint,4,2,6,maxint}//6
	};

	printf("Prim点选取顺序为:\n");
	prim(c, N);

	return 0;
}

在这里插入图片描述


3、Kruskal算法的实现(选边)

图G=(V,E),其中V为所有结点的集合,E为图G的边的集合

采用Kruskal算法时,采用了并查集作为辅助的数据结构
并查集中两个重要的实现函数:

  • int find_root(int x):查找以结点x的根结点;
  • int union_set(int x,int y):合并x,y所在的两个子集,若两个子集存在回路返回0,不存在回路返回1;

思路:

  • 将图G的边集按照权值大大小升序排列
  • 每次选取未被选取过,且权值最小的边,若选取该边构成回路,则舍弃该边,选择下一条权值最小的边;
  • 直到图G所有的顶点在一个连通分量上;

如图所示,描述了Kruskal算法选取顶点的过程:
在这里插入图片描述
具体实现过程:

边的结构体

typedef struct edge
{
	int x;
	int y;
	int weight;
	char name;
	int selected;
}Edge;

并查集的函数

int parent[N];//记录顶点的父结点
int rank[N];//记录以顶点为根的树的深度(压缩路径)

//初始化parent[]和rank[]
void init()
{
	for (int i = 1; i < N; i++)
	{
		parent[i] = -1;//初始时,每个顶点的父结点默认为-1
		rank[i] = 1;
	}
}

//查找顶点x的根结点
int find_root(int x)
{
	int x_root = x;
	while (parent[x_root] != -1)
		x_root = parent[x_root];
	return x_root;
}

//合并两个顶点所在的子集,合并成功返回1,失败返回0
int union_set(int i, int j)
{
	int i_root = find_root(i);
	int j_root = find_root(j);

	if (i_root == j_root)
		return 0;//i,j在同一个子集中,合并失败

	//i,j不在同一个子集中,合并两个子集
	if (rank[i_root] > rank[j_root])
		parent[j_root] = i_root;
	else if (rank[i_root] < rank[j_root])
		parent[i_root] = parent[j_root];
	else
	{
		//i,j深度相同的情况下
		parent[i_root] = j_root;
		rank[j_root]++;
	}
	return 1;
}

Kruskal实现

//n是边的个数
void kruskal(Edge e[], int n)
{
	init();
	qsort(e, 11, sizeof(e[1]), cmp);//升序

	for (int i = 1; i <= n; i++)
	{
		int x = find_root(e[i].x);
		int y = find_root(e[i].y);

		if (x != y)
		{
			//无回路,选中此边
			e[i].selected = 1;
			union_set(e[i].x, e[i].y);
			printf("%c ", e[i].name);
		}
	}
	/*
	for (int i = 1; i <= n; i++)
		if (e[i].selected)
			printf("%c ", e[i].name);*/
}

测试函数

int main()
{
	Edge e[11] = {//10条边
		{0},//此条数据不用
		{1,4,5,'a',0},{4,6,2,'b',0},{6,5,6,'c',0},{5,2,3,'d',0},{2,1,6,'e',0},
		{1,3,1,'f',0},{3,4,5,'g',0},{3,6,4,'h',0},{3,5,6,'i',0},{3,2,5,'j',0}
	};

	int n = 10;

	kruskal(e, n);

	return 0;
}

在这里插入图片描述


4、Dijkstra算法的实现(选点)

图G=(V,E),其中V为所有结点的集合,E为图G的边的集合

采用辅助数组dist[i],来存储从源点到i的最短距离

思路:

  • 从集合V-S中选取顶点j,满足dist[j]=min{dist[i]|i属于V-S},并将顶点j加入到集合s中
  • 集合s扩充后,修改源点出发到V-S上任一顶点k的最短路径,dist[j]+c[j][k]<dist[k](这里是Dijkstra区别于Prim的最重要一点)
  • 重复上述步骤,知道所有顶点都在集合S中

如图示

在这里插入图片描述
具体实现:

//dist[i]存储从源点到i的最短路径
void dijkstra(int c[][N], int dist[], int start)
{
	int s[N];

	//初始化参数
	for (int i = 1; i < N; i++)
	{
		dist[i] = c[start][i];
		s[i] = 0;
	}
	s[start] = 1;

	//每次循环箱集合s中添加一个顶点
	for (int i = 2; i < N; i++)
	{
		int tmp = maxint;
		int u = start;

		//做贪心选择,选择非集合s中顶点到源点最小的权值边的顶点
		for (int j = 1; j < N; j++)
		{
			if (!s[j] && dist[j] < tmp)
			{
				//记录
				u = j;
				tmp = dist[j];
			}
		}

		s[u] = 1;

		//更新dist[]的值
		for (int j = 1; j < N; j++)
			if (!s[j] && (dist[u] + c[u][j] < dist[j]))
				dist[j] = dist[u] + c[u][j];

	}
}

main函数中的测试:

int main()
{
	int c[N][N] = {
		{0},//0号下标不使用
		{0,0,10,maxint,maxint,5},
		{0,maxint,0,1,maxint,2},
		{0,maxint,maxint,0,4,maxint},
		{0,7,maxint,6,0,maxint},
		{0,maxint,3,9,2,0}
	};//5个顶点,0号下标不使用

	int dist[N], start = 1;

	dijkstra(c, dist, start);

	for (int i = 1; i < N; i++)
		printf("dist[%d]=%d\t", i, dist[i]);
	printf("\n");

	return 0;
}

在这里插入图片描述



http://www.niftyadmin.cn/n/784539.html

相关文章

互斥锁(下):如何用一把锁保护多个资源?

之前的文章里也说了&#xff0c;一把锁可以保护多个资源&#xff0c;所以受保护的资源和锁之间合理的关联关系应该是N&#xff1a;1的关系&#xff0c;上次我们直说了如何正确保护一个资源&#xff0c;但是没说如何正确保护多个资源&#xff0c;我们上次最后一个案例也说&#…

死锁的出现原因与预防

上一篇文章中&#xff0c;我们用串行的方法解决了非并发的问题&#xff0c;利用互斥锁锁住整个类或者this对象将关联和不关联的问题去解决&#xff0c;但是如果在并发的环境下&#xff0c;还用串行代码设计的方法去写产品&#xff0c;势必会让整个项目的执行效率就降低&#xf…

阿里开源框架Dubbo的入门及原理分析

文章目录Socket网络传输Socket问题所在WebServiceRPCDubbo利用Zookeeper辅助Dubbo完成监听dubbo-zk-demo https://github.com/2NaCl/dubbo-demo 我们一般调用一个jvm里的类或者方法&#xff0c;往往都是直接new&#xff0c;然后调用&#xff0c;但是dubbo的意义在于&#xff0…

高性能网络应用框架Netty

Netty是一个高性能网络应用框架&#xff0c;应用也十分普遍&#xff0c;目前在Java领域中&#xff0c;Netty基本上可以成为网络程序的标配了。Netty框架功能丰富也十分复杂&#xff0c;此篇专栏主要会分析Netty框架中的线程模型&#xff0c;而线程模型也直接影响了网络程序的性…

linux下启动redis

redis-server etc/redis.conf 后面的配置文件也可以不指定 redis-cli -h {host} -p {port} 用redis客户端链接redis转载于:https://www.cnblogs.com/zwsblogs/p/9008245.html

结构性学习Java的设计模式

设计模式是人们为软件开发中相同表征的问题&#xff0c;抽象出的可重复利用的解决方案&#xff0c;设计模式上已经代表了一些特定情况的最佳实践&#xff0c;同时也起到了软件工程师之间沟通“行话”的作用。理解和掌握典型的设计模式&#xff0c;有利于我们提高沟通、设计的效…

(C/C++)-图的深度优先遍历(DFS)和广度优先遍历(BFS)

(C/C)-图的深度优先遍历(DFS)和广度优先遍历(BFS) 1、图的深度优先遍历(DFS) 图的深度优先遍历与树的先序遍历类似&#xff0c;即尽可能深的遍历图 这里采取邻接矩阵存储图&#xff0c;存储的图如下&#xff1a; ps: 这个图沿用我的上一篇文章(最小生成树和单源最短路径)&a…

leetcode-62-dp经典算法题笔记

然后开始分析一下&#xff0c;首先知道&#xff0c;这题肯定是dp思路的&#xff0c;虽然也能回溯&#xff0c;但是主要还是说dp&#xff0c;做个笔记。 做dp题最重要的就是写出状态转移方程。 我先举几个例子&#xff0c;从中寻找一下规律&#xff0c;因为dp思想规律都是差不…