685. 冗余连接 II

/**
 * 685. 冗余连接 II
 * @author wsq
 * @date 2020/09/17
	在本问题中,有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点。
	输入一个有向图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
	结果图是一个以边组成的二维数组。 每一个边 的元素是一对 [u, v],用以表示有向图中连接顶点 u 和顶点 v 的边,其中 u 是 v 的一个父节点。
	返回一条能删除的边,使得剩下的图是有N个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

	示例 1:
	输入: [[1,2], [1,3], [2,3]]
	输出: [2,3]
	解释: 给定的有向图如下:
	  1
	 / \
	v   v
	2-->3
	
	示例 2:
	输入: [[1,2], [2,3], [3,4], [4,1], [1,5]]
	输出: [4,1]
	解释: 给定的有向图如下:
	5 <- 1 -> 2
	     ^    |
	     |    v
	     4 <- 3
	
	链接:https://leetcode-cn.com/problems/redundant-connection-ii
 */
package com.wsq.leetcode;

import java.util.Arrays;

public class FindRedundantDirectedConnection {
	/**
	 *	题解参照:
	 *	https://leetcode-cn.com/problems/redundant-connection-ii/solution/bing-cha-ji-java-by-liweiwei1419/
	 * @param edges
	 * @return
	 */
    public int[] findRedundantDirectedConnection(int[][] edges) {
    	// 边数统计
    	int len = edges.length;
    	// step1  入度统计
    	int[] inDegree = new int[len + 1];
    	for(int[] edge: edges) {
    		inDegree[edge[1]]++;
    	}
    	
    	// step2: 删除入度为2的边,看看是否成形成环
    	// 由于题目要求删除靠后的边,因此这里删除的时候从后向前删除
    	for(int i = len - 1; i >= 0; i--) {
    		if(inDegree[edges[i][1]] == 2) {
    			if(!judgeCircle(edges, len, i)) {
    				return edges[i];
    			}
    		}
    	}
    	// step3:再尝试删除入度为1的边
		for(int i = len -1; i >= 0; i--) {
			if(inDegree[edges[i][1]] == 1) {
				if(!judgeCircle(edges, len, i)) {
					return edges[i];
				}
			}
		}
		throw new IllegalArgumentException("输入不符合要求。");
    }
    /**
     *	检验除了 将要删除的边,其他边是否构成环
     * @param edges
     * @param len 节点总数
     * @param removeEdgeIndex 删除的边的下标
     * @return 构成环,返回true,否则返回False
     */
    public boolean judgeCircle(int[][] edges, int len, int removeEdgeIndex) {
    	// 由于树节点是从1开始的,为了方便,使得创建父节点数组时多创建一个
    	UnionFind unionFind = new UnionFind(len + 1);
    	for(int i = 0; i < len; i++) {
    		if(i == removeEdgeIndex) {
    			continue;
    		}
    		if(!unionFind.union(edges[i][0], edges[i][1])) {
    			return true;
    		}
    	}
    	return false;
    }
    
    public class UnionFind{
    	// 存储父节点
    	private int[] parent;
    	
    	public UnionFind(int n) {
    		parent = new int[n];
    		for(int i = 0; i < n; i++) {
    			parent[i] = i;
    		}
    	}
    	/**
    	 *	寻找x的根节点,并且将所在路径进行压缩
    	 *	最终目的就是将所有的节点都直接指向根节点
    	 * @param x
    	 * @return
    	 */
    	public int find(int x) {
    		while(x != parent[x]) {
    			// 路径压缩(隔代压缩)
    			parent[x] = parent[parent[x]];
    			x = parent[x];
    		}
    		return x;
    	}
    	/**
    	 *	合并两节点
    	 * @param x
    	 * @param y
    	 * @return 如果合并成功返回true
    	 */
    	public boolean union(int x, int y) {
    		int rootX = find(x);
    		int rootY = find(y);
    		
    		if(rootX == rootY) {
    			return false;
    		}
    		
    		parent[rootX] = rootY;
    		return true;
    	}
    }
    
    public static void main(String[] args) {
    	FindRedundantDirectedConnection f = new FindRedundantDirectedConnection();
    	int[][] edges = {{1, 2}, {2, 3}, {3, 1}, {1, 4}};
        int[] res = f.findRedundantDirectedConnection(edges);
        System.out.println(Arrays.toString(res));
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值