最小生成树算法(Prim算法与Kruskal算法)

10 篇文章 0 订阅
10 篇文章 0 订阅

Prim算法(稠密图)

算法步骤

1. 将1号节点加入最小生成树
2. 寻找不在生成树中的顶点到生成树的最短边    // O(n) ,可用小根堆优化为O(log m)
3. 将其加入最小生成树
4. 更新所有节点到最小生成树的距离
5. 重复2-4操作,直到循环n次,或者第二步得到的最短边为INF时 结束循环。

C++代码如下

const int N=510,INF=0x3f3f3f3f;
int g[N][N],dist[N];
int n,m;
bool st[N]; // 是否在最小生成树中

int prim(){
    memset(dist,0x3f,sizeof dist);
    int ans=0;
    for(int i=0;i<n;i++){   	// 循环n次
        int t=-1;
        for(int j=1;j<=n;j++){		// 寻找不在生成树中的顶点到生成树的最短边
            if(!st[j] && (t==-1||dist[t]>dist[j]))t=j;
        }
        if(i&& dist[t]==INF)return INF;  // 第二步得到的最短边为INF时 结束循环。
        st[t]=true;  				// 将其加入最小生成树
        
        if(i)ans+=dist[t];			// 若不是第一点,则计算其长度
        
        for(int j=1;j<=n;j++){		// 更新到最小数的最短距离,有的可能最INF
            dist[j]=min(dist[j],g[t][j]); // 这里不用dist[t]+g[t][j],因为不是从起点出发
        }
    }   
    return ans;
}

算法评价

Prim算法使用于稠密图,时间复杂度为O(n^2),可用小根堆优化为O(nlogm)

Kruskal算法

算法步骤

1. 建立并查集,并为每个点建立一个集合  		// `O(n)`
2. 将所有边按权重从小到大排序			//`O(mlogm)`
3. 依次扫描每个边(x,y,w)	//  `O(m)`
	若x,y属于同一集合(连通),则忽略这条边,扫描下一条	
	否则,合并x,y所在的集合,将w加到答案中
4. 所有边扫描完,则构成了最小生成树		
5. 若加入最小生成树的边小于n,则代表给出的图不是连通图

C++代码

const int N=1e5+10,M=2e5+10,INF=0x3f3f3f3f;

struct Edge{
    int a,b,w;
}edges[M];
int n,m;
int ans=0;
int cnt=0;
int p[N]; // 并查集

int find(int x){
    if(x!=p[x])p[x]=find(p[x]);
    return p[x];
}

int cmp(Edge a,Edge b){
    return a.w<b.w;
}

void kruskal(){
    for(int i=1;i<=n;i++)p[i]=i;  // 初始化并查集 并赋值
    sort(edges+1,edges+m+1,cmp);     // 对所有边排序(下标从1开始存)
    for(int i=1;i<=m;i++){   // 扫描每条边
        int a=edges[i].a,b=edges[i].b,w=edges[i].w;
        a=find(a),b=find(b);
        if(a==b){				// 若两个顶点属于一个集合,则扫描下一个集合
            continue;
        }
        else {					// 若两个顶点不属于同一个集合,则将w加入到答案中
            p[a]=b;
            ans+=w;
            cnt++;
            
        }
    }
    if(cnt<n-1)ans=INF;
}

算法评价

很显然,算法的时间复杂度为O(n)或者O(mlogm),一般用Kruskal存稀疏图,m<n2 所以最终的时间复杂度为O(mlogm)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值