最小生成树

问题引入

一个有n个节点m条边(m>=n)且边有权值的无向图,若从这m条边中选出n-1条边,将这n个点连接起来组成一棵树,且所选边的权值和最小,那么这棵树称为该无向图的最小生成树(Minimum Spanning Tree,MST)。
拿实际问题来举例。假如有n个地点,现在要修路将这n个地点相互连通,每条路连通两个地点,但不同的路造价不一样。问如何修路,使得所有地点都能连通,但路的造价最低。


Kruskal算法

首先假设所有的边均未被选择,然后将所有的边按权值从小到大排序,并从权值最小的边开始逐个边检验:若该边加入到图中后未出现环,则该边保留到图中,否则就舍弃该边,然后再检验下一条边,直到选出n-1条边。

对于环的判断,可使用并查集进行处理。假设当前检验的边连接的两点存在相同的根节点,则说明这两点在加入该边前就已经连通,则加入此边后必定有环。

LeetCode 1584

题目链接:https://leetcode.com/problems/min-cost-to-connect-all-points/

给出n个点,现在要用一些边连接这些点,并令这些点相互连通,求需要的最短边长。连接两点 ( x i , y i ) (x_i, y_i) (xi,yi) ( x j , y j ) (x_j, y_j) (xj,yj)的边的长度为: ∣ x i − x j ∣ + ∣ y i − y j ∣ |x_i-x_j|+|y_i-y_j| xixj+yiyj

struct edge {
    short i, j;
    int dist;
};

int f[1005];

int Cmp(const void * a, const void * b) {
    return (*(struct edge *)a).dist - (*(struct edge *)b).dist;
}

int FindRoot(int x) {
    return x == f[x] ? x : (f[x] = FindRoot(f[x]));
}

int minCostConnectPoints(int** p, int n, int* pointsColSize){
    struct edge allEdge[n*n];
    int nEdge = 0, ans = 0, usedEdge = 0;
    int num[n];
    for (int i=0; i<n; i++) {
        f[i] = i;
        num[i] = 1;
    }

    for (int i=0; i<n; i++)
        for (int j=i+1; j<n; j++) {
            allEdge[nEdge].i = i;
            allEdge[nEdge].j = j;
            allEdge[nEdge].dist = abs(p[i][0]-p[j][0]) + abs( p[i][1]-p[j][1]);
            nEdge++;
        }

    qsort(allEdge, nEdge, sizeof(allEdge[0]), Cmp);
    
    //以树的节点数作为判断树大小的依据进行合并,同时进行路径压缩。
    for (int i=0; i<nEdge; i++) {   
        int r1 = FindRoot(allEdge[i].i);
        int r2 = FindRoot(allEdge[i].j);
        if (r1 != r2) {
            if (num[r1] > num[r2]) {
                num[r1] += num[r2];
                f[r2] = r1; 
            }
            else {
                num[r2] += num[r1];
                f[r1] = r2; 
            } 
            ans += allEdge[i].dist;
            usedEdge++;
            if (usedEdge == n-1)
                return ans;
        }
    }
    return ans;
}



Prim算法

从任意一点开始,然后搜索与之相连的点中边权值最小的连接,然后对于这两个点都搜索相连的点,如果某个点连上后有环则放弃该点。若有权值相同的两点则任选一个(因为最后所有点都是要搜索其相邻点的)。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值