【MST最短路径】Prim算法和Kruskal算法

最小生成树问题有两种经典的算法,分别是Prim和Kruskal,他们的代码有很多种,但要使复杂度更低,算法的具体实现过程还是值得讨论的:

这里分别给出相对复杂度更低一点的代码:

Prim:

struct node{
	int u,w;
	node(){};
	node(int a, int b)
	{
		this->u = a;
		this->w = b;
	}//构造函数
	bool operator<(const node& a)
	const{
		return this->w > a.w
	}//比较运算函数用以建优先队列
}Edge[101];

struct node2{
	int len;
	int v[101];
	int w[101];
}V[101];

上述步骤已经顺利写出了两组数据结构,第一组是储存待会构造优先队列的数据,第二组是储存我们输入的边、点集,接下来是主函数:

int main()
{
	int N,i,a,b,c;
	for(i = 0; i <= 100; i++)
		V[i].len = 0;
	scanf("%d",&N);
	while(scanf("%d%d%d",&a,&b,&c)
	{
		V[a].v[len] = b;
		V[a].w[len++] = c;
		V[b].v[len] = a;
		V[b].w[len++] = c;
	}
	int U[101],key[101],p[101];
	//U用来标记是否已经遍历,key用来标记当前点的最短邻接边,p用来标记点的最短路径所连接的端点
	for(i = 1; i <= N; i++)
	{
		U[i] = 0;
		p[i] = 0;
		key[i] = MAX_N;
	}
	key[a] = 0;//从a开始选路线
	priority_queue<node> Q;
	Q.push(node(a,0));
	while(!Q.empty())
	{
		node E = Q.top();
		Q.pop();
		if(U[E.u] != 0)
			continue;
		U[E.u] = 1;
		for(i = 0; i < V[E.u].len; i++)
		{
			int u = E.u;
			int v = V[E.u].v[i];
			int w = V[E.u].w[i];
			if(U[v] != 0)
				continue;
			if(key[v] < w)
			{
				key[v] = w;
				Q.push(node(v,w));
				p[v] = u;
			}
		}
	}
	for(i = 1; i <= N; i++)
	{
		printf("%d %d %d\n",i,p[i],key[i]);
	}
	return 0;
}

Kruskal:
接下来我们再来探讨一下Kruskal算法,Kruskal算法的思想就是先把边按照权值排序,遍历的时候按照边的权值大小来取边,只要不形成回路,这条边就能使用:
先来构造数据结构:

struct node {
	int u,v,w;
	node(){};
	node(int a, int b, int c)
	{
		this->u = a;
		this->v = b;
		this->w = c;
	}
	bool operator< const(node &a)
	const{
		return this->w > a.w
	}
}Edge[101];

int p[x], rank[x];//后述并查集需要用到

这样我们的数据结构就已经构造完毕了,接下来是输入的过程:

int main()
{
	int N,i,a,b,c;
	scanf("%d",&N);
	std::priority_queue<node> Q;
	while(scanf("%d%d%d", &a, &b, &c) != EOF)
	{
		Q.push(node(a,b,c));
	}

用优先队列直接将每一轮输入压入队列中,在队列中针对权值大小对每一条边进行排序,接下来是取边,取边的时候我们需要保障不形成回路,这里就要用到并查集的思想,每一个相连通的点都在同一个Union中,如果一条边的两个点不在一个Union中,那么我们就可以取这条边:

	for(i = 0; i <= N; i++)
	{
		p[i] = i;
		rank[i] = 0;
	}
	int s = 0;
	while(!Q.empty())
	{
		node V = Q.top();
		Q.pop();
		int u = Q.u;
		int v = Q.v;
		int w = Q.w;
		if(Find(u) != Find(v))//判定u和v是否在同一个Union中,如果不在,则执行下面程序
		{
			Link(Find(u), Find(v));
			Edge[s].u = u;
			Edge[s].v = v;
			Edge[s++].w = w;
		}
	}
	for(i = 0; i < s; i++)
	{
		printf("%d %d %d", Edge[i].u, Edge[i].v, Edge[i].w);
	}
	return 0;
}

到此我们的主函数就结束了,下面介绍一下并查集:
并查集就是集合,把一些特定相关的元素放到同一个集合中,比如此处的相连通的点,就应该放在同一个集合中,如果下次取边的时候恰好边的两端点在同一个集合中,那么就说明如果把这条边加上去图就形成回路了,岂不是一种很方便的操作?

补充:
Find函数:

int Find(int x)
{
	if(p[x] == x)
		return x;
	else
		return (Find(p[x]));
}

Link函数:

void Link(int x, int y)
{
	if(rank[x] == rank[y])
	{
		p[x] = y;
		rank[y]++;
	}
	else if(rank[x] > rank[y])
	{
		p[y] = x;
	}
	else if(rank[x] < rank[y])
	{
		p[x] = y;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值