最小生成树的两种算法,prim 与 kruskal的实现

最小生成树的生成有两种方法,一种是prim算法,另一种是kruskal算法。数据结构和算法导论的学习中了解到一点,整理出来方便以后瞅瞅。

无向图的生成方式

采用链式前向星的方法,用一个数组来表示边的所有起点,结构体来表示点所能到达的边,迭代构成一个无向图。

#include <iostream>
#include <vector> 
using namespace std;

#define MAX 100000
#define MAX_VAL 0x00ffffff

//储存无向图的每个点可以到达的点 
struct node
{
	int to;//顶点的一个终点 
	int val;//边的权值 
	int next;//同一顶点的下一个终点 
}dir[MAX];

vector<int> path;//依次生成最小树的节点的顺序 
int head[MAX] = {0};//头结点的 

//链式存储无向图 
void add(int from,int to,int val,int len)
{
	dir[len].to = to;
	dir[len].val = val;
	dir[len].next = head[from];
	head[from] = len;
} 

int main()
{
	int m;//点的范围
	cin>>m;
	 
	int n;//边的数目
	cin>>n;
	
	int len = 1;
	for(int i = 1; i <= n; i++)
	{
		int u,v,val;
		cin>>u>>v>>val;
		add(u,v,val,len);//u -> v 
		len++;
		add(v,u,val,len);//v -> u
		len++;
	}
	
	Prim(n,m);
//	cout<<endl;
//	for(int i = 1; i <= n; i++)
//	{
//		for(int j = head[i]; j != 0; j = dir[j].next)
//			cout<<dir[j].to<<' '<<dir[j].val<<' '<<i<<endl;
//	}
	
	//依次打印生成树节点的顺序 
	for(int i = 0; i < path.size(); i++)
		cout<<path[i]<<endl;
	
	return 0;
}
 

prim算法

算法思想

选择一个节点作为树的根节点,然后把这个节点放入一个已经找到的集合里,然后开始迭代,每一次都从这个集合中的点,依次找到一条集合中点可以到达的最小的边,然后把这条边的终点记录在集合中,继续重复迭代,直到所有的点都选择完毕。
时间复杂度:因为是两层循环,所以说时间复杂度还是O(n2)。
在这里插入图片描述

算法实现

//选择以1号点为树的跟节点 
void Prim(int n,int m)
{
	int visit[MAX] = {0};//判断找到树节点
	
	path.push_back(1); 
	visit[1] = 1;
	
	//依次找到其他的最小节点 
	for(int i = 1; i < m; i++)
	{
		 int min_val = MAX_VAL;
		 int min_to = 0;//本次循环可以找到的最小边 
		 //从所有已经选择的点开始遍历 
		 for(int j = 0; j < path.size(); j++)
		 {
		 	int from = path[j];
		 	//查找该点可以到达的点,找到一条最小的边 
		 	for(int k = head[from]; k != 0; k = dir[k].next)
		 	{
		 		int to = dir[k].to;
		 		//如果该点没有被访问,并且边的权值小于当前找到的,进行更新 
		 		if(!visit[to] && min_val > dir[k].val)
		 		{
		 			min_val = dir[k].val;
		 			min_to = to; 
				}
			}
		 }
		 
		 if(min_val == MAX_VAL)
		 {
		 	cout<<"无法生成最小树"<<endl;
			return ; 
		 } 
		 
		 visit[min_to] = 1; 
		 path.push_back(min_to);
	}
} 

kruskal算法

算法思想

首先先对所有的边从小到大排序,然后在保证没有回路的前提下选出权重娇小的n-1条边,如果(i,j)是所有集合中没有选择的边中权重最小的,并且(i,j)不会和已选的边构成回路。如果(i,j)的两个点 i 和 j 同属于一个连通分支,那么选择 i 和 j 会构成回路,反之则不会。

连通分支

对于一个无向图而言,它的一个极大连通子图即为一连通支。比如说,一个图由三部分构成,其中每一部分都是连通的,但三个部分之间互相不连通,那么每一部分即为无向图的一个连通分支。此图的连通分支数为3。

时间复杂度:给每个边排序的时间复杂度为O(n * logn),选择边的时候时间复杂度是 O(logn),所以说总的时间复杂度是O(n * logn)。
在这里插入图片描述

算法实现

int GetRoot(int k, vector<int>& V)
{
	while (k != V[k])
		k = V[k];//只有根节点才满足 k = V[k]
	return k;
}

//Kruskal 算法
double Kruskal(vector<side>& sides, vector<point>& points)
{
	double sum = 0;//定义最后生成树的边的长度
	//从小到大给边排序
	sort(sides.begin(), sides.end(), cmp);

	int pointNum = points.size();
	vector<int> V(pointNum, 0);//定义并查集数组
	//初始化每一个结点都是根节点,并查集中
	for (int i = 0; i < pointNum; i++)
		V[i] = i;

	//筛选
	int len = sides.size();//边的数目
	for (int i = 0; i < len; i++)
	{
		int start = GetRoot(sides[i]._start, V);
		int end = GetRoot(sides[i]._end, V);
		if (start != end)
		{
			V[start] = end;
			sum += sides[i]._distance;
		}
	}
	return sum;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值