首页 > 编程语言 >MST算法-堆优化Prim与并查集优化Kruskal

MST算法-堆优化Prim与并查集优化Kruskal

时间:2022-10-25 14:23:47浏览次数:97  
标签:结点 Prim int 查集 最小 生成 算法 顶点 优化

生成树

首先,生成树是相对于连通图来说的。它是一个连通图的子图,而且没有环,也可以看成是一棵树。对于生成树,其所有结点的根节点都是同一个。
生成树有以下两个性质:

  1. 生成树包含了图的所有结点
  2. 生成树只包含图的最小数量的边

由于生成树包含了图的所有结点,且只包含最小数量的边。所有,对于一个顶点数为n的图,其生成树只包含n-1条边

最小生成树

而最小生成树是相对于加权无向连通图的,加权即所有的边都有一个权重值,它可能代表了从结点u到结点v的代价或者成本。最小生成树是图的所有生成树中,边的权重之和最小的那个。
最小生成树对于路径规划、NP难问题的近似求解、图片分割、聚类分析都有重要的作用。
需要注意的是,最小生成树可能不唯一。在一个加权图中,如果存在权重相等的边,那么就有可能生成不同的最小生成树。但是如果每条边的权重都不同,那么生成的最小生成树就是唯一的。

MST算法

问题定义:
输入:加权无向连通图$G(V,E,W)$
输出:最小生成树$T(V,E*,W*)$

求解策略:

  1. 从一个空子图开始
  2. 每次往子图添加一条边
  3. 判断新的子图是否是某一最小生成树的一部分:
    1. 保证子图仍然是一棵树,没有环
    2. 边的权重足够小

Prim算法

对于图G,Tv是最小生成树的一部分结点,Sv是图中剩余结点,Sv=V-Tv。Prim算法通过不断选择Tv和Sv中两个边权重最小的结点u和v,将结点v和这条权重最小的边加入到最小生成树的结点中去。不断重复这个过程直到所有结点都加入到最小生成树中。

算法实现

算法对于输入中可能的重边,取重复边中权重最小的那条边:g[a][b] = g[b][a] = min(g[a][b], c);

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
// 创建矩阵
const int N = 5200,INF = 0x3f3f3f3f;
int vertexNum, edgeNum;

int g[N][N], dist[N];
bool st[N];
int prim() {
     // dist进行填充
    memset(dist, 0x3f, sizeof dist);
    cout << sizeof dist << endl;
    dist[1] = 0;
    int res = 0;
    // 寻找最短的边且不够成环, 即寻找未加入的且最小权值的边
    for (int i = 1; i <= vertexNum; i++) {
        int t = -1;
        for (int j = 1; j <= vertexNum; j++)
            if (!st[j] && (t == -1 || dist[j] < dist[t])) //找到一个未访问过的顶点,并且其权值最小
                t = j;
        if (dist[t] == INF) return -1;
        //表示此边加入最小生成树中,状态改为true
        st[t] = true;
        res += dist[t];
        //更新dist,更新顶点t到其他顶点的距离,如果比dist
        for (int j = 1; j <= vertexNum; j++)
            if (!st[j] && dist[j] > g[t][j])
                dist[j] = g[t][j];
    }

    return res;
}

int main() {
    //将输入值赋予vertexNum与edgeNum,表示节点数与边
    cin >> vertexNum >> edgeNum;
    // 对邻接矩阵初始化,全部赋予极大值
    memset(g, 0x3f, sizeof g);
    //依次读入edgeNum个边
    while (edgeNum--) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);//可能会有重边,对于重边取权重最小的边
    }

    int res = prim();
    if (res == -1) cout << "orz" << endl;
    else cout << res << endl;

    return 0;
}

运行结果
image.png

算法分析

算法读取输入中边权重的时间复杂度为$O(E)$,生成N-1条边时,每次都需要遍历所有未加入生成树的顶点,时间复杂度为$O(V2)$。算法总时间复杂度为$O(E+V2)=O(V^2)$。

最小堆优化

可以使用最小堆算法来优化查找最轻边的过程,将未加入生成树的各顶点到生成树中顶点的最短距离生成一个最小堆,每次选择最小堆的根结点加入生成树中即可,然后判断加入的结点和边是否会成环即可。
原始算法使用遍历来查找一个到生成树的边权重最小的顶点,时间复杂度为$O(V)$。而取最小堆根结点时间复杂度为$O(1)$, 调整最小堆时间复杂度为$O(\log V)$。对于结点较多的稠密图,可以缩短遍历顶点的运行时间。

Kruskal算法

Kruskal算法也是实现MST的一种算法,他通过对边按照权重进行排序,不断将权重最小的边加入到生成树中。对将要加入的边进行判断,其是否会使生成树有环。如果判断结果为是则丢弃该边,并重新寻找另一条权重最小的边。

使用并查集优化

可以使用并查集算法优化寻找当前结点的根结点的过程,来判断将要加入的边是否会使最小生成树成环。其算法思路如下:

  1. 对于每一个将要加入最小生成树的边,需要找该边的两个结点点的根结点。如果一样,说明已经连通,不能连接,否则说明可以加入最小生成树。
  2. 对于每条加入最小生成树的结点,都设置已经在生成树顶点集合U中的顶点为父结点。如果两个点都是第一次进集合,那么随机取一个结点为父结点。
  3. 对于每一次成功的连接,如果是两个家族(多条边和顶点)连接在一起,那么其中一个家族的根结点,认另外一个家族的根结点为父结点。

可见,只要根结点相同,连接起来一定成环。通过并查集算法可以简化最小生成树算法中,加入的边是否会使生成树成环的过程。

算法实现

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 5200,INF = 0x3f3f3f3f;


struct edge{
    int x,y,v; //x,y存储边的两个端点 v存储边的长度
};

struct edge edges[200005];
int n,m,weight;
int pred[5005]; //并查集操作中存储顶点i的父亲顶点pred[i]

void quicksort(int l, int r){ //快速排序,将存边的数组按边值升序排序
    int i,j,m,temp;
    i = l;
    j = r;
    m = edges[(l+r) / 2].v;
    while (i <= j){
        while (edges[i].v < m)
            i++;
        while (edges[j].v > m)
            j--;
        if (i <= j){
            edge temp=edges[i];
            edges[i]=edges[j];
            edges[j]=temp;
            i++;
            j--;
        }
    }
    if (l < j)
        quicksort(l, j);
    if (i < r)
        quicksort(i, r);
}

int search(int x){ //并查集中的寻找根顶点的操作
    if (pred[x] == x)
        return x;
    else
        return pred[x] = search(pred[x]); //一种路径压缩的手段
}

int main(){
    weight = 0;
    int n,m;
    cin >> n >> m;
    for (int i = 0; i < m; i++){
        cin >> edges[i].x >> edges[i].y >> edges[i].v; //读入边集,这里不对重边进行特殊处理
    }
    quicksort(0, m-1); //对边进行排序
    for (int i = 1; i <= n; i++)
        pred[i] = i; //对父亲顶点数组进行初始化
    for (int i = 0; i < m; i++){
        if (search(edges[i].x) == search(edges[i].y))//搜索该边的两个顶点的祖父顶点,判断是否相同
            continue; //如果该边的两个顶点已经在生成树中,则舍去这条边
        pred[search(edges[i].x)] = search(edges[i].y); //否则将这条边加入生成树(运用并查集的合并集合操作)
        weight = weight + edges[i].v; //记录最小生成树的长度
    }
    int dup=0;
    for (int i = 1; i <= n; i++)
        if(pred[i] == i)dup++; //判断祖先顶点是他本身的顶点个数
    if(dup>1){         //如果祖先顶点是他本身的顶点数大于1,说明有顶点未加入生成树,不是连通图
        cout << "orz" << endl;
        return 0;
    }
    cout << weight << endl;
    return 0;
}

运行结果:
image.png

总结

由于Kruksal算法是对边进行操作,先取出边,然后判断边的两个节点,这样的话,如果一个图结构非常的稠密,那么Kruksal算法就比较慢了,而Prim算法只是对节点进行遍历,并使用set进行标记,因此会相对于Kruksal算法,在稠密图方面好很多。因此Kruksal算法常用于稀疏图,而Prim算法常用于稠密图。

标签:结点,Prim,int,查集,最小,生成,算法,顶点,优化
From: https://www.cnblogs.com/d42z/p/16824693.html

相关文章

  • HDU 4135 Co-prime
    题目链接:​​传送门​​多组数据问区间内与互质的数的个数区间问题显然要转化成两个区间相减的问题也就是的答案减去的答案这里反过来求不互质的数的个数筛法可以提示我......
  • 索引优化分析
    一、性能下降SQL慢、执行时间长、等待时间长查询语句写得烂索引失效关联查询太多join设计缺陷或不得已的需求)服务器调优及各个参数设置(缓冲、线程数......
  • 李超树(斜率优化无脑DS)
    如果我们需要一个数据结构来维护多个线段在一个点上的最值,那我们就可以使用李超树来完成这个事情。李超树的每个区间记录的是中点值最大的一条线段。如何做到呢?1、插入s......
  • POJ 2588(解析几何+并查集)
    题目就是早从左到右的路注意输入的实数这题图画好就行,别像我一开始把图弄反就成从上开始找,若找到一个与下边界相邻的就无解,找到与左边相邻的记圆与左边界相交的下边的点(相当......
  • React性能优化的8种方式
    一引沿Fiber架构是React16中引入的新概念,目的就是解决大型React应用卡顿,React在遍历更新每一个节点的时候都不是用的真实DOM,都是采用虚拟DOM,所以可以理解成fiber就是R......
  • 一文读懂云渲染“串流”全链路时延及优化策略
    ​这是一个让云游戏完美起步的时代。云游戏作为产业内近年来炙手可热的话题,具有“云端运行、超高清、零延时、即点即玩”等众多特性。随着5G时代的到来,以及中心云......
  • Mysql性能优化(三)
     如何对一条查询语句进行性能分析,必不可少的要使用的是explain,explain的意思是执行计划;那接下来我们就详细说明一下explain的返回结果;一、explain的使用方法explainse......
  • 858 Prim算法求最小生成树
    #include<bits/stdc++.h>usingnamespacestd;constintN=510,INF=0x3f3f3f3f;intn,m;intg[N][N];//储存边intdist[N];//储存点到集合的距离intst[N......
  • 大数据计算,如何优化SQL?
    前言很多大数据计算都是用SQL实现的,跑得慢时就要去优化SQL,但常常碰到让人干瞪眼的情况。很多大数据计算都是用SQL实现的,跑得慢时就要去优化SQL,但常常碰到让人干瞪眼的情......
  • mysql的not in 优化
    有一个项目,mysql语句采用了notin,结果某些页面打开需要40多秒,排查sql语句后,发现是采用了notin语法导致全表扫描,消耗了大量的时间,飘易记录下优化的过程:项目简介:会议应......