[路飞]_leetcode-685-冗余连接 II

348 阅读1分钟

题目描述

[题目地址]

在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。

输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1 到 n)的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 ui 是 vi 的一个父节点。

返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

示例 1:

输入: edges = [[1,2],[1,3],[2,3]]
输出: [2,3]

示例 2:

输入: edges = [[1,2],[2,3],[3,4],[4,1],[1,5]]
输出: [4,1]

提示:

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

解题思路

本题依然是一个连通性问题,只不过在连通性的基础上添加了有向性,使问题变得更复杂了。
本题中有向图的连接方式可以分为三种类型,要根据连接方式分类讨论其返回值。

  1. 示例1 中的连接情况,有一个子节点有两个父节点,定义为双入度情况
  2. 示例2 中的连接情况,连接形成了环,定义为成环情况
  3. 示例中未给出的情况,即成环,又有双入度的情况

针对双入度的情况,需要返回的边是形成双入度的边。
针对成环的情况,需要返回的边是成环的边。
针对即成环,又有双入度的情况,需要返回的是双入度边的子节点及其父节点组成的边。

演示

c1.png

c2.png

c3.png

c4.png

c5.png

代码实现

 /*
    考虑三种情况:
        1.附加边指向了根节点,导致所有的节点都有一个父节点,导致出现环;
        2.附加边没有指向根节点,会出现树中某个节点会有两个父节点;也有可能会出现环路
 */
var findRedundantDirectedConnection = function(edges) {
    // 先要获取节点的个数,就是边的个数
    let nodeCount = edges.length;
    // 根据节点个数构造并查集,长度+1,避免从0开始
    let uf = new UnionFind(nodeCount + 1);
    // 记录产生父节点
    let parent = [];//记录每一个节点的父节点是多少
    for(let i = 1; i < (nodeCount + 1); i++){//边的长度+1
        parent[i] = i;//做一个初始化
    }
    let conflit = -1; // 来记录一下是否产生了双重父节点的情况
    let cycle = -1; //产生了环路
    for(i in edges){
        let edge = edges[i];
        let node1 = edge[0],node2 = edge[1]; //拿到了2个结点
        if(parent[node2] != node2){//node2这个结点有2个父节点
            conflit = i;//这是第几组数据,记录下来
        }else{ //否则没有双重父节点,就把他们连起来
            parent[node2] = node1;
            if(uf.findSet(node1) === uf.findSet(node2)){//出现了环路
                cycle = i;
            }else{ // 既有环路,又有双重结点
                uf.unite(node1,node2)
            }
        }
    }
    if(conflit < 0){ // 没有双重父节点产生,就把环路记录下来
        return edges[cycle]; //只有环路
    }else{ // 否则只有双重父节点产生
        let conflitEdge = edges[conflit];
        // 还需判断是否有环
        if(cycle >= 0){
            return [parent[conflitEdge[1]],conflitEdge[1]];// 有两个入度点
        }else{
            return conflitEdge;
        }
    }
};

// 无环,但是有两个入度点
class UnionFind{
    constructor(n){
        this.parent = [];
        for(let i = 0; i <= n ; i++){
            this.parent[i] = i;
        }
    }
    findSet(index){
        if(this.parent[index] != index){
            this.parent[index] = this.findSet(this.parent[index])
        }
        return this.parent[index];
    }
    unite(index1,index2){
      this.parent[this.findSet(index1)] = this.findSet(index2);
    }
}

至此我们就完成了leetcode-685-冗余连接 II
欢迎小伙伴建议讨论