leetcode-684. 冗余连接

题目

在本问题中, 树指的是一个连通且无环的无向图。

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

结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

示例 1:

输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
  1
 / \
2 - 3

示例 2:

输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
    |   |
    4 - 3

注意:

输入的二维数组大小在 3 到 1000。
二维数组中的整数在1到N之间,其中N是输入数组的大小。

解题思路

暴力版:
从最后面向前一个一个删边,如果删掉这个边后,图中无环,则找到了目标边。所以可以将这个问题转换为求无向图中环的问题。

求无向图中的环,用DFS,在DFS栈里保存当前节点的深度,如果新访问到的节点以前访问过,并且节点的深度和原先的节点深度差别不是2的话,那么就出现了环。

这里的2怎么来的呢?因为题目限制了不会有1 -> 3 -> 1这样的环,所以最短的环必然会是1 -> 3 -> 2 -> 1,这种时候从1再次走到1,走过的路径长度为3,而从1走到3再回头走到1的路径长度是2,所以可以用这种方法判断无向图中是否存在环。

除了用路径长度,还可以在栈中保存parent对应的节点,来判断当前的节点是父节点还是环导致的重复出现。

对每次删完边的图,建图需要 o ( N ) o(N) o(N)的复杂度,DFS需要 o ( N ) o(N) o(N)的复杂度,所以总的时间复杂度是 o ( N 2 ) o(N^2) o(N2)

并查集:
每个点是一个集合,从前向后遍历边,把新的边并到并查集里面,第一个还没有并就已经存在的边就是答案。因为这道题说明了答案只能有1个,所以可以这样做,如果有多条边则不能直接返回。
并查集的时间复杂度就比较低了。对n个元素的并查集进行一次操作的复杂度是 o ( α ( n ) ) o(\alpha(n)) o(α(n)),最坏要进行N次操作,所以总的时间复杂度是 o ( N α ( N ) ) o(N\alpha(N)) o(Nα(N))

代码

暴力版:

class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        def exist_loop(graph: dict) -> bool:
            visited_nodes = {}
            while len(visited_nodes) < len(graph):
                remain_nodes = list(set(graph) - set(visited_nodes))
                stack = [(remain_nodes[0], 0)]
                while stack:
                    node, depth = stack.pop()
                    if node in visited_nodes:
                        if visited_nodes[node] != depth - 2:
                            return True
                        continue
                    visited_nodes[node] = depth
                    stack += [(neighbor, depth + 1) for neighbor in graph[node]]
            return False
        # build graph
        raw_graph = {}
        for from_node, to_node in edges:
            if from_node not in raw_graph:
                raw_graph[from_node] = []
            raw_graph[from_node].append(to_node)
            if to_node not in raw_graph:
                raw_graph[to_node] = []
            raw_graph[to_node].append(from_node)
        for index in range(len(edges) - 1, -1, -1):
            dropped_graph = raw_graph.copy()
            from_node, to_node = edges[index]
            dropped_graph[from_node].remove(to_node)
            dropped_graph[to_node].remove(from_node)
            if not exist_loop(dropped_graph):
                return edges[index]

并查集版:

class UnionFindSet:
    def __init__(self, N: int) -> None:
        self.parent = [i for i in range(N + 1)]
        self.height = [0] * (N + 1)
    def find(self, x: int) -> int:
        if self.parent[x] == x:
            return x
        self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    def union(self, x: int, y: int) -> None:
        x, y = self.find(x), self.find(y)
        if x == y:
            return
        if self.height[x] < self.height[y]:
            self.parent[x] = self.parent[y]
        else:
            self.parent[y] = self.parent[x]
            if self.height[x] == self.height[y]:
                self.height[x] += 1

class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        union_find_set = UnionFindSet(len(edges))
        for each_edge in edges:
            if union_find_set.find(each_edge[0]) == union_find_set.find(each_edge[1]):
                return each_edge
            union_find_set.union(each_edge[0], each_edge[1])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值