Leetcode 261.以图判树

Time: 20190903
Type: Medium

题目描述

给定从 0 到 n-1 标号的 n 个结点,和一个无向边列表(每条边以结点对来表示),请编写一个函数用来判断这些边是否能够形成一个合法有效的树结构。

示例 1:

输入: n = 5, 边列表 edges = [[0,1], [0,2], [0,3], [1,4]]
输出: true
示例 2:

输入: n = 5, 边列表 edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
输出: false
注意:你可以假定边列表 edges 中不会出现重复的边。由于所有的边是无向边,边 [0,1] 和边 [1,0] 是相同的,因此不会同时出现在边列表 edges 中。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/graph-valid-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

按照题目的要求可以得到一个无向图,判断这个无向图是不是树。是树的话满足两个要求:

  • 全部结点互相链接
  • 图中无环

用并查集的思路是最直接的,初始时将结点的父节点都定义为自身,在最初Union边的两个结点时,结点是属于不同的子集的,一旦二者的大Boss是一样的,即属于同一个子集,表示有环存在。

这个结论第一次听起来很奇怪,但是跟踪一下合并过程就清楚了。

开始时是散落的多个结点,开始选取其中两个连线,会出现多个小分组,每个分组内的结点的大Boss一样。此时如果选中的两个结点来自同一个分组,Union起来一定是有环的。因此,我们在Union方法中做判断即可。

Find方法有不同的写法,递归或者非递归。递归的话自带路径压缩,迭代算法需要自己写路径压缩。并查集是需要路径 压缩的,即让一个子集内的结点统一战线。

代码

极简版的并查集算法(不含权重)。

class DSU:
    def __init__(self):
        self.par = list(range(5001)) # 初始化时父亲结点指向自身
        self.rnk = [0] * 5001

    def find(self, x):
        if self.par[x] != x:
            self.par[x] = self.find(self.par[x])
        return self.par[x]

    def union(self, x, y):
        xr, yr = self.find(x), self.find(y)
        if xr == yr:
            return False
        elif self.rnk[xr] < self.rnk[yr]:
            self.par[xr] = yr
        elif self.rnk[xr] > self.rnk[yr]:
            self.par[yr] = xr
        else:
            self.par[yr] = xr
            self.rnk[xr] += 1
        return True
    
class Solution:
    def validTree(self, n: int, edges: List[List[int]]) -> bool:
        uf = DSU()
        for e in edges:
            # 判断有没有环
            if(uf.union(e[0], e[1]) == False): 
                return False
        
        # 图可能没有连通,例如[[1, 2], [3, 4]] 
        # 需要判断是不是所有节点的父亲都一样
        par = uf.find(0)
        for i in range(1, n):
            if par != uf.find(i): 
                return False
        return True

相似题目

Leetcode 323. 无向图中连通分量的个数

都是用并查集的典型算法。

END.

你可能感兴趣的:(LeetCode)