leetcode | 685. Redundant Connection Ⅱ

题目

In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus every node has exactly one parent, except for the root node which has no parents.

The given input is a directed graph that started as a rooted tree with N nodes (with distinct values 1, 2, …, N), with one additional directed edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed.

The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] that represents a directed edge connecting nodes u and v, where u is a parent of child v.

Return an edge that can be removed so that the resulting graph is a rooted tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array.

Example 1:

Input: [[1,2], [1,3], [2,3]]
Output: [2,3]
Explanation: The given directed graph will be like this:
  1
 / \
v   v
2-->3

Example 2:

Input: [[1,2], [2,3], [3,4], [4,1], [1,5]]
Output: [4,1]
Explanation: The given directed graph will be like this:
5 <- 1 -> 2
     ^    |
     |    v
     4 <- 3

Note:
The size of the input 2D-array will be between 3 and 1000.
Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array.

思路与解法

这道题目与684. Redundant Connection题目大致相同,都是从原图中删除一条边,使得原图转化为一颗树,所以输入的图一定是连通图。不过,该图中的树边为有向边。所以,原来的思路不能够直接照搬下来,而要做一些调整。
首先,我们可以大致将输入数据分为如下两种情况:

  1. 所有节点的入度都为1
    0L2xpdXloNzM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  2. 存在某个节点的入度为2
    在这里插入图片描述
    以上两种情况便包含了输入数据的所有可能,因为不可能存在某个节点的入度大于2的情况。如果入度大于2,则说明该节点存在三个父亲节点(可能相同),删除一条边之后,该节点仍然还有两个父亲节点,则不满足题目条件,无法构成一颗有向树
    对于第一个图中所描述的情况,我们仍然可以用并查集的思想来进行判别,代码与684. Redundant Connection方法二相同,不在详细介绍。
    对于第二个图中所描述的情况,我们可以任意去掉入度为2的节点的一条入边,假设为edge,然后从某个入度为0的节点利用BFS遍历该有向图,如果所有节点被访问了一次,则证明删除掉edge后,形成了一颗有向树;否则,如果BFS结束后存在某些节点没有被访问到,则剩余节点形成了环,说明删除edge并没有形成有向树,此时应该删除另外一条入边。

代码实现

此算法我才用go语言实现:

func findRedundantDirectedConnection(edges [][]int) []int {
   /*
    * graph 存储反向图(类似于邻接矩阵),用于获得入度为2的节点的其中1条入边,另外一条在输入时进行保存
    * graph 存储正向图(类似于邻接表),用于遍历该图
    * node_count 存储节点数(等于边数)
    * in_degree 统计每个节点入度
    * double_in_degree 存储入度为2的节点
    * candidate 存储候选的边(用于删除),即为入度为2的节点的其中1条入边
    */
    graph := make(map[int]int)
    graph2 := make(map[int][]int)
    nodes_count := len(edges)
    in_degree := make([]int, nodes_count+1)
    double_in_degree := -1
    candidate := -1
    /* 初始化graph和graph2,统计入度,并且获得double_in_degree和candidate */
    for _, edge := range edges {
        if in_degree[edge[1]] > 0 {
            double_in_degree = edge[1]
            candidate = edge[0]
        } else {
            graph2[edge[0]] = append(graph2[edge[0]], edge[1])
            graph[edge[1]] = edge[0]
            in_degree[edge[1]]++
        }
    }
	/* 对应于第一个图中的情况 并查集 */
    if double_in_degree == -1 {
        parents := make([]int ,len(edges)+1)
        for _, edge := range edges {
            parent0 := findParent(edge[0], parents)
            parent1 := findParent(edge[1], parents)

            if parent0 == parent1 {
                return edge
            }
            parents[parent1] = parent0
        }
    } else {
        /* 对应于第二个图中的情况 BFS便利图*/
        queue := make([]int, 0)
        visited := make([]int, 0)
		
        for i:=1; i<=nodes_count; i++ {
            if in_degree[i] == 0 {
                queue = append(queue, i)
                visited = append(visited, i)
            }
        }
        /* 如果存在2个及以上的入度为0的节点,则candidate并不满足题目要求(因为即使删除该边,也不能形成一个有向树) */
        if len(queue) > 1 {
            return []int{graph[double_in_degree], double_in_degree}
        }

        for len(queue)!=0 {
            head := queue[0]
            queue = queue[1:]
            for _, next := range graph2[head] {
                in_degree[next]--
                if in_degree[next] ==0 {
                    queue = append(queue, next)
                    visited = append(visited, next)
                }
            }
        }
        /* 遍历完整个图,形成一棵有向树 */
        if len(visited) == nodes_count {
            return []int{candidate, double_in_degree}
        }
        return []int{graph[double_in_degree], double_in_degree}
    }
    return []int{}
}

func findParent(node int, parents []int) int {
    if parents[node] == 0 {
        return node
    }
    return findParent(parents[node], parents)
}

评测结果

在这里插入图片描述
从上图可以看出,代码的执行效率还是很高的。但是,由于整个图存储了两遍,所以所耗费的空间有些多,希望以后可以进行改进。

猜你喜欢

转载自blog.csdn.net/liuyh73/article/details/83188107