Leetcode685 冗余连接II 题解思路及实现

题目

在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。
输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1 到 n)的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 ui 是 vi 的一个父节点。
返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

在这里插入图片描述
在这里插入图片描述

提示:
n == edges.length
3 <= n <= 1000
edges[i].length == 2
1 <= ui, vi <= n

思路

形成有根树的条件:

  • 非根节点有且仅有一个父节点
  • 根节点没有父节点

那么产生附加冗余边的情况有:

  • 非根节点有两个父节点 -> 可能有环也可能无环
  • 根节点拥有父节点 -> 一定存在环

记第一种情况为导致冲突的边,第二中情况为导致环路的边,由于附加边只有一条,需要在两种边中进行判断。
若图中没有产生环路:一定发生了冲突,断冲突边
若图中存在环路: 既有环路又有冲突,断环路边;只有环路没有冲突,断开环路中任一条边

从出入度角度考虑

有根树

  • 根节点:入度为0,出度>=1
  • 非根节点:入度为1

冗余情况

  • 产生冲突没有环路:存在一个节点的入度为2
  • 产生冲突存在环路:存在一个节点的入度为2且该节点入度的两边一条为环路边,一条为冲突边
  • 无冲突存在环路:除以上情况外的剩余情况,环路上节点的入度为1,出度>=1

方法

采用并查集,并查集常常用来判断图中是否存在环

代码实现

class Solution {
    public int[] findRedundantDirectedConnection(int[][] edges) {
        int n = edges.length;
        int[] res = new int[2];
        int[] fa = new int[n + 1];
        int[] indegree = new int[n + 1];
        int[] outdegree = new int[n + 1];
        for(int i = 0; i < n + 1; i++){
            indegree[i] = 0;
            outdegree[i] = 0;
        }
        for(int i = 0; i < n; i++){
            indegree[edges[i][1]]++;
            outdegree[edges[i][0]]++;
        }
        //存在冲突边
        int node1 = -1;
        for(int i = n; i > 0; i--){
            if(indegree[i] > 1){
                node1 = i;
                break;
            }
            
        }
        
        List<Integer> list = new ArrayList<>();
        
        for(int i = n - 1; i >= 0; i--){
            if(edges[i][1] == node1){
                list.add(i);
            }
        }   
        
        for(int i = 0; i < list.size(); i++){
            //删除入度节点为2的节点对应的两条边中任一条,判断此时是否能构成有根树
            if(isTreeAfterDelete(edges, fa, n, list.get(i))){
                return edges[list.get(i)];
            }

        }

        //不存在冲突但存在环路

        List<Integer> node2 = new ArrayList<>();
       
        for(int i = n; i > 0; i--){
            if(indegree[i] > 0 && outdegree[i] >= 1){
                //node2存放环路中的每个节点
               node2.add(i);
                 
            }
        
        }
        List<Integer> list2 = new ArrayList<>();
        
        for(int i = n - 1; i >= 0; i--){
            for(int j = 0; j < node2.size(); j++){
                if(edges[i][0] == node2.get(j)){
                list2.add(i);
            }

            }
            
        }    
        
        for(int i = 0; i < list2.size(); i++){
            if(isTreeAfterDelete(edges, fa, n, list2.get(i))){
                return edges[list2.get(i)];
            }

        }

        return res;    
        
    }

    void init(int[] fa, int n){
        for(int i = 0; i < n; i++){
            fa[i] = i;
        }
    }

    int find(int[] fa, int i){
        if(fa[i] == i) return i;
        fa[i] = find(fa, fa[i]);
        return fa[i];
    }

    void join(int[] fa, int i, int j){
        int v = find(fa, i);
        int u = find(fa, j);
        if(v == u) return;
        fa[u] = v;
    }

    boolean same(int[] fa, int i, int j){
        int v = find(fa, i);
        int u = find(fa, j);
        return v == u;
    }

    boolean isTreeAfterDelete(int[][] edges, int[] fa, int n, int deleteEdge){
        init(fa, n + 1);
        for(int i = 0; i < edges.length; i++){
            if(i != deleteEdge){
                int s = edges[i][0];
                int d  =edges[i][1];
                if(same(fa, s, d)){
                    return false;
                } else {
                    join(fa, s, d);
                }
            }
        }
        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值