图论——最小生成树

No.1 概念

想了解最小生成树,首先得明白什么是生成树。

生成树的概念:包含连通图中所有的顶点,且任意两顶点之间有且仅有一条通路。

那什么是最小生成树?

最小生成树(Minimum Spanning Trees)的概念:连通图的一颗生成树(Spanning Tree)是包含图的所有顶点的连通无环子图(也就是一棵树)。加权连通图的一颗最小生成树是图的一颗权重最小的生成树,其中,树的权重定义为所有边的权重总和。最小生成树问题就是求一个给定的加权连通图的最小生成树问题。

直接地讲,它就是在无向图中选择边把图的所有的节点连接成一棵树,要求边权之和最小。


No.2 Prim算法

1)Prim算法是什么?

Prim算法通过一系列不断扩张的子树来构造一棵最小生成树。

2)Prim算法的思想

  1. 我们从图的顶点集合中任意选择的一个单项点,作为序列中的初始子树。
  2. 每一次迭代时,以一种贪心的思想来扩张当前的生成树,即把不在树的最近顶点添加到树中(我们所说的最近顶点,是指一个不在树中的顶点,它以一条边权最小的边和树中的顶点相连,而树的形状是无所谓的)。
  3. 当图的所有顶点都包含在所构造的树中以后,该算法就停止了。

3)Prim算法需注意的点

其实Prim算法的思想和Dijkstra相像,都是分两部分来操作。

4)举栗

如图,我们确定一个起点编号1,将1加入1号阵营。设变量mst求最小生成树。mst此时为0。

顶点编号12345
连接边24511
边权12123
阵营10000

找到与1相连边权最小的顶点2,加入1号阵营,mst+=1。

顶点编号12345
连接边24511
边权12123
阵营11000

找到与2相连边权最小的顶点4,加入1号阵营,mst+=2。

顶点编号12345
连接边24531
边权12123
阵营11010

找到与4相连边权最小的顶点3,加入1号阵营,mst+=2。

顶点编号12345
连接边24531
边权12123
阵营11110

找到与3相连边权最小的顶点5,加入1号阵营,mst+=1。

顶点编号12345
连接边24531
边权12123
阵营11111

此时,所有顶点已加入确定最小生成树的阵营,退出循环,答案mst为6.

5)Prim算法的优化

Prim算法思想上与Dijkstra算法相似,优化也区别不大。

我们可以定义一个优先队列,队列中元素记录了节点的编号和节点和树中的顶点相连的边权,将源点压入队列。当队列非空,执行以下操作:

1.u等于队顶的节点,w等于队顶节点的最短边权。

2.遍历u的所有边,如果能找到节点v小于v的当前值,更新v,将v压入队列。

以上就实现了优化。

6)核心代码

Code


void Prim(int s){
	memset(d,0x3f,sizeof(d));
	memset(vis,0,sizeof(vis));
	d[s]=0;
	q.push(node(s,0));
	while(!q.empty()){
		int u=q.top().u;
		q.pop();
		if(vis[u]){
			continue;
		}
		mst+=d[u];
		vis[u]=1;
		for(int i=0;i<Graph[u].size();i++){
			int v=Graph[u][i].v;
			long long w=Graph[u][i].w;
			if(d[v]>w){
				d[v]=w;
				q.push(node(v,w));
			}
		}
	}
}

7)Prim算法总结

Prim算法优化后节省了很多时间,用于稠密图,也就是边多的图较好。


No.3 Kruskal算法

1)Kruskal算法是什么?

Kruskal算法按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路

2)Kruskal算法的思想

使用并查集,(并查集是什么?-Link)将加权图每个顶点都看做森林,然后将图中每条邻接边的边权按照升序的方式进行排列,接着从排列好的邻接边表中抽取边权最小的边,写入该边的起始顶点和结束顶点,连接顶点将森林构成树,然后读取起始结束顶点的邻接边,优先抽取边权小的邻接边,继续连接顶点将森林构成树。

添加邻接边的要求是加入到图中的邻接边不构成回路(环)。如此反复进行,直到已经添加n-1条边为止。

3)Kruskal算法的目标

Kruskal根据边权以递增的方式逐渐建立最小生成树,是以边为目标去构建最小生成树。

4)核心代码

Code

 void Kruskal(){
    sort(ed+1,ed+m+1,cmp);
    for(int i=1;i<=m;i++){
        int p=FindSet(ed[i].u);
        int q=FindSet(ed[i].v);
        if(p!=q){
            UnionSet(p,q);
            mst+=ed[i].bq;
            if(si[q]==n){
                return;
            }
        }
    }
}

5)Kruskal算法总结

Kruskal算法易理解,易上手,时间复杂度也很低,用于稀疏图,也就是点多的图较好。


完结!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值