数据结构(C++)Kruskal算法

今天晚上终于处理好了上周一直留下的问题,把Kreskal算法给解决了,主要利用的是并查集的思想,再辅助一些代码就可以实现。
不得不佩服自己思路的跨越,周五到周日三天去监考四六级,周四写的代码,周一晚上再回来看,居然还能一直想着思路,脑子里可能只有数据结构吧,现在要好好学离散数学

基本思想

  1. 建立一个无向连通图 G=( V , E ),这里字母就完全按照上课时候的课件来标注了。设最小生成树为T = { U ,TE },其初态为 U = V , TE={ } 。
  2. 按照图中,两点之间的边权进行从小到大的排序
    2.1. 如果被考察的两条边属于两个不同的连通分量,则把此边纳入最小生成树之中,成为最小生成树中的一条边(加入T中),与此同时把这两个不同的连通分量合并成一个连通分量。
    2.2. 如果被考察边属于同一个连通分支,舍去此条边。(离散数学中讲到过,树中两点间加入一条边,则会产生回路。)
  3. 循环进行操作,直到T中连通分量数为1,此时树之中所有的点都是连通的,且由于每次都是纳入的最小边,所以边权最小,成为最小生成树。

并查集

parent数组的含义是一个节点如果与另外一个结点连接后,我们默认一个结点的序号是另一个结点的parent数组值,这里是顺序存储的双亲表示法中相关知识。
这里处理的是判断一条边的两个点是否属于同一个连通分支,利用while循环不断地找最终指向的父亲,这样我们找的每个结点都进行并查集的搜索,就可以判断两结点是否是同一个连通分支。

int Find(int parent[], int node) 
{
	int f = node;
	while (parent[f] > -1)
	{
		f = parent[f];
	}
	return f;
}

在这里插入图片描述
在这里插入图片描述
上面的两张就是第一个图的并查集搜索过程,这里都是用的序号作为的顶点值,这里顶点值与并查集的顺序存储序号相同,比较方便查找。

Kruskal算法

  1. 对边进行从小到大排序,使用sort函数。先从最小边开始查找判断,并且为所有的parent数组赋初值-1,表示所有的顶点都没有父亲,都是一个独立的连通分支。
  2. 进行arcNum次循环,分别判断边两个顶点是否是同一个连通分支后,如果不是输出该边的两个顶点,以及这条边的边权,并且为其中赋值parent,使这两个顶点成为同一个连通分支。
  3. 如果次数等于顶点数-1,直接跳出循环,遍历完成。
void Kruskal()
{
	sort(edge + 1, edge + arcNum + 1, cmp);
	for (int i = 1; i <= vertexNum; ++i)
	{
		parent[i] = -1;
	}
	int count = 0;
	int start, end;
	for (int i = 1; i <= arcNum; ++i)
	{
		start = edge[i].start;
		end = edge[i].end;
		int m = Find(parent, start);
		int n = Find(parent, end);
		if (m != n)
		{
			cout << start << "  " << end << "  " << edge[i].value << endl;
			parent[n] = m;
			count++; // 执行到n-1次后直接结束 n是顶点个数
			if (count == vertexNum - 1)
				break;
		}
	}
}

全部代码

这里与Prime不同的地方是,Prime使用的邻接矩阵存储,这里用了struct结构体,因为如果使用结构体在sort函数中能够方便排序,时间减少。

#include<iostream>
#include<algorithm>
using namespace std;
int Find(int parent[], int node) 
{
	int f = node;
	while (parent[f] > -1)
	{
		f = parent[f];
	}
	return f;
}
struct EdgeNode
{
	int start;
	int end;
	int value;
};
bool cmp(EdgeNode& a, EdgeNode& b)
{
	return a.value < b.value;
}
class MGraph
{
	int arcNum;
	int vertexNum;
	int *parent;
	EdgeNode *edge;
public:
	MGraph(int n, int e)
	{
		arcNum = e;
		vertexNum = n;
		parent = new int[vertexNum + 1];
		edge = new EdgeNode[arcNum + 1];
		for (int i = 1; i <= arcNum; ++i)
		{
			cin >> edge[i].start >> edge[i].end >> edge[i].value;
		}
	}
	void Kruskal()
	{
		sort(edge + 1, edge + arcNum + 1, cmp);
		for (int i = 1; i <= vertexNum; ++i)
		{
			parent[i] = -1;
		}
		int count = 0;
		int start, end;
		for (int i = 1; i <= arcNum; ++i)
		{
			start = edge[i].start;
			end = edge[i].end;
			int m = Find(parent, start);
			int n = Find(parent, end);
			if (m != n)
			{
				cout << start << "  " << end << "  " << edge[i].value << endl;
				parent[n] = m;
				count++; // 执行到n-1次后直接结束 n是顶点个数
				if (count == vertexNum - 1)
					break;
			}
		}
	}
};
int main()
{
	int x, y;
	cin >> x >> y;
	MGraph G(x, y);
	G.Kruskal();
	return 0;
}

时间复杂度是O(E log(E)),时间花费在排序上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值