最小生成树——Kruskal算法

目录

基本思想

实现

伪代码

实际问题求解


最小生成树:带权连通图的生成树中 边的权值之和最小的生成树。

  • 最小生成树不是唯一的。当图中的各边权值互不相等时,最小生成树是唯一的;
  • 若无向连通图本身是一棵树时(边数比顶点数少1 ),则最小生成树就是它本身。
  • 最小生成树的边数为顶点数减1。

基本思想

(找权值最小的边)

  1. 初始时为只有n 个顶点而无边的非连通图 T 每个顶点自成一个连通分量,然后按照边的权值由小到大的顺序;
  2. 不断选取当前未被选取过且权值最小的边,若该边依附的顶点落在 T 中不同的连通分量上,则将此边加入 T , 否则舍弃此边而选择下一条权值最小的边。以此类推,直至T中所有顶点都在一个连通分量上。

  •  通常在Kruskal算法中,采用堆来存放边的集合,因此每次选择最小权值的边只需O(log|E|)的时间。此外,由于生成树中的所有边可视为一个等价类,因此每次添加新的边的过程类似于求解等价类的过程,由此可以采用并查集的数据结构来描述从而构造广的时间复杂度为O(|E|log|E|)。(|E|表示边数)
  • Kruskal算法适合于边稀疏而顶点较多的图。

实现

伪代码

void Kruskal(V,T) {
    T=V;  			//初始化树,仅含顶点
    numS=n; 		//连通分量数
    while(numS>l){	//若连通分量数大于1
        从E中取出权值最小的边(v,u); 
        if(v和 u 属于T 中不同的连通分量){ 
            T=TU{ (v,u) };//将此边加入生成树中
            numS-; 		//连通分量数减1
        }
    }
}

实际问题求解

 P1546 [USACO3.1]最短网络 Agri-Net

题目背景:Farmer John 被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。

题目描述:FJ 已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。

你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过。

(采用基本Kruskal算法求解:Prim算法求解见http://t.csdn.cn/OFNwt

public class MST {
    class Edge{
        int x,y,wei;
        public Edge(int x,int y,int wei){
            this.x=x;
            this.y=y;
            this.wei=wei;
        }
    }
    //并查集
    class UFSets{
        private int[] vex;
        public UFSets(int n){
            vex=new int[n];
            for (int i = 0; i < n; i++) {
                vex[i]=i;
            }
        }
        //x的最高祖宗
        public int find (int x){
           return vex[x]==x?x:find(vex[x]);
        }
        //合并
        public void union (int x,int y){
            int fx=find(x);
            int fy=find(y);
            if (fx!=fy){
                vex[fx]=fy;
            }
        }
    }
    public int kruskal(int[][] w){
        int n=w.length;
        //最小生成树总权值
        int result=0;
        //顶点集初始化
        UFSets vex=new UFSets(n);
        List<Edge> list=new ArrayList<>();
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                if (w[i][j]>0){
                    list.add(new Edge(i,j,w[i][j]));
                }
            }
        }
        list.sort(new Comparator<Edge>() {
            @Override
            public int compare(Edge o1, Edge o2) {
                return o1.wei-o2.wei;
            }
        });
        //选n个边
        for (Edge e : list) {
            if (n<=0){
                break;
            }
            //边的两个点属于不同连通分量,则加入这条边
            if (vex.find(e.x)!=vex.find(e.y)){
                vex.union(e.x,e.y);
                result+=e.wei;
                n--;
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Scanner scan=new Scanner(System.in);
        int n=scan.nextInt();
        int[][] w=new int[n][n];
        for (int i = 0; i < w.length; i++) {
            for (int j = 0; j < w.length; j++) {
                w[i][j]=scan.nextInt();
            }
        }
        System.out.println(new MST().kruskal(w));
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SS上善

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值