图论——最小生成树

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。

顶点编号 1 2 3 4 5
连接边 2 4 5 1 1
边权 1 2 1 2 3
阵营 1 0 0 0 0

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

顶点编号 1 2 3 4 5
连接边 2 4 5 1 1
边权 1 2 1 2 3
阵营 1 1 0 0 0

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

顶点编号 1 2 3 4 5
连接边 2 4 5 3 1
边权 1 2 1 2 3
阵营 1 1 0 1 0

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

顶点编号 1 2 3 4 5
连接边 2 4 5 3 1
边权 1 2 1 2 3
阵营 1 1 1 1 0

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

顶点编号 1 2 3 4 5
连接边 2 4 5 3 1
边权 1 2 1 2 3
阵营 1 1 1 1 1

此时,所有顶点已加入确定最小生成树的阵营,退出循环,答案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算法易理解,易上手,时间复杂度也很低,用于稀疏图,也就是点多的图较好。


完结!

 

posted @   faith_xy  阅读(195)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示