每日一题 21.01.21 LeetCode 1489. 找到最小生成树里的关键边和伪关键边 java题解

题目

https://leetcode-cn.com/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/
在这里插入图片描述
在这里插入图片描述

方法一:枚举 + 最小生成树判定

知识

1.克鲁斯卡尔算法:在E中选择代价最小的边,若该边依附的顶点分别在T中不同的连通分量上,则将此边加入到T中;否则,舍去此边而选择下一条代价最小的边。依此类推,直至T中所有顶点构成一个连通分量为止。

2.连通分量:
在这里插入图片描述

分析

1.首先对原图执行 Kruskal 算法,得到最小生成树的权值value。
2.,枚举每条边,判断是否关键边。需要注意的是,关键边也满足伪关键边对应的性质。所有先判断关键边,如果不是才去判断伪关键边。
在这里插入图片描述
3.如果不是关键边,判断是否伪关键边。在这里插入图片描述

代码

class Solution {
    public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) {
        int m=edges.length;
        int[][] newEdges=new int[m][4];
        for(int i=0;i<m;i++){
            for(int j=0;j<3;j++){
                newEdges[i][j]=edges[i][j];
            }
            newEdges[i][3]=i;//边的序号
        }
        Arrays.sort(newEdges,(u,v)->(u[2]-v[2]));//按边的权值升序
        //求最小生成树的权值value
        UnionFind uf=new UnionFind(n);
        int value=0;
        for(int i=0;i<m;i++){
            if(uf.union(newEdges[i][0],newEdges[i][1])){
                value+=newEdges[i][2];//加上这条边的权值
            }
        }
        //结果
        List<List<Integer>> res=new ArrayList<>();
        res.add(new ArrayList<Integer>());
        res.add(new ArrayList<Integer>());

        for(int i=0;i<m;i++){
            //判断是否关键边
            UnionFind ud=new  UnionFind(n);
            int v=0;
            for(int j=0;j<m;j++){
                //i!=j:不要i这条边
                //加入newEdge[j]这条边
                if(i!=j&&ud.union(newEdges[j][0],newEdges[j][1])){
                    v+=newEdges[j][2];
                }
            }
            //不连通 或 连通但v变大
            //i是关键边
            if(ud.setCount!=1||(ud.setCount==1&&v>value)){
                res.get(0).add(newEdges[i][3]);
                continue;//不需要判断非关键边了
            }
            //判断是否伪关键边
            ud=new UnionFind(n);
            //在计算最小生成树的过程中,最先考虑这条边,
            //即最先将这条边的两个端点在并查集中合并
            ud.union(newEdges[i][0],newEdges[i][1]);
            v=newEdges[i][2];//初始总权值:这条边的权值
            for(int j=0;j<m;j++){
                //除了i这条边
                //加入其他边
                if(i!=j&&ud.union(newEdges[j][0],newEdges[j][1])){
                    v+=newEdges[j][2];
                }
            }
            if(v==value){
                res.get(1).add(newEdges[i][3]);
            }
        }
        return res;
    }
}
// 并查集模板
class UnionFind{
    public int[] parent;
    public int[] size;
    public int setCount;//当前连通分量数目
    public UnionFind(int n){
        this.parent=new int[n];
        this.size=new int[n];
        Arrays.fill(size,1);
        this.setCount=n;
        for(int i=0;i<n;i++){
            parent[i]=i;
        }
    }
    public int find(int index){
        if(parent[index]!=index){
            parent[index]=find(parent[index]);
        }
        return parent[index];
    }
    public boolean union(int index1,int index2){
        int a=find(index1);
        int b=find(index2);
        if(a==b) return false;
        if(size[a]<size[b]){
            int tmp=a;
            a=b;
            b=tmp;
        }
        parent[b]=a;
        size[a]+=size[b];
        setCount--;
        return true;
    }
    public boolean connected(int index1,int index2){
        int a=find(index1);
        int b=find(index2);
        if(a==b)
            return true;
        return false;
    }
}

复杂度

在这里插入图片描述

结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值