并查集-leetcode题目总结

Posted BJUT赵亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集-leetcode题目总结相关的知识,希望对你有一定的参考价值。

本文记录了有关并查集在leetcode中的相关情况,如果有同学在做相关的内容,可以邮件(zhaoliang619960421@outlook.com)或微信(BestCoder_BestLife)与我联系

本文参考了以下的相关文档,在此对文档作者表示感谢:
https://zhuanlan.zhihu.com/p/93647900
https://blog.csdn.net/dingdingdodo/article/details/106272854
https://segmentfault.com/a/1190000022952886
https://blog.csdn.net/dingdingdodo/article/details/104290972

并查集定义

并查集解决的问题是,在给定的图中找到哪些节点属于同一个联通分量,减少查询的次数。用来处理一种可以转换为图结构中的合并、查询相关的问题。
为了实现这一目的,所以需要将每个节点定义一个父节点(初始化为自己),并且通过父节点的父节点可以一直的溯源到相关跟节点,通过合并union函数将两个节点合并在一起,具体合并在一起的意义就是,将其中一个节点的设置为另外一个节点的父节点。利用合并的方式,会将相关的一系列节点串起来,构造成一棵很深的树,要查选一个节点的根节点时,需要反复的溯源父节点的父节点,会增加查询的耗时,为了解决这个问题的方法就是路径压缩和安秩合并,查用的就是是路径压缩方法。路径压缩的意义就是将一棵很深的树,通过递归的方式,将这个树上的每个节点的父节点都定义到跟节点上,这样在进行查询的只需要O(1)的时间复杂度。

并查集模板

class UnionFind:
    def __init__(self,size):
    	# 定义每个节点的父节点
        self.father = 
    def find(self,x):
    	# 初始化时,将每个节点的父节点定义为自己
    	self.father.setdefault(x,x)
    	# 第一次查询时,父节点是自己,直接返回自己
        if self.father[x] == x:return x 
        # 当该节点已经和其他节点相连接,已经属于某一个联通分量了
        # 此时找父节点,需要一直的追溯父节点的父节点
        # 将最终的父节点(根节点)赋值给当前节点的父节点
        # 这样就实现了路径压缩,每次在查询的时候将并没有连接到根节点的节点的父节点连接到跟节点
        self.father[x] = self.find(self.father[x])
        return self.father[x]
    def union(self,x,y):
    	# 将两个节点合并在同一个联通分量里
    	# 需要将一个节点父节点,连接到另外一个节点的父节点上
        self.father[self.find(x)] = self.find(y)
    def is_connected(self,x,y):
    	# 当两个节点是同一个父节点时,则两个节点属于同一个联通分量
    	# 每次在find的时候都会返回当前联通分量的根节点
        return self.find(x) == self.find(y)

leetcode题目

684 冗余链接

class UnionFind:
    def __init__(self,size):
        self.father = 
    def find(self,x):
    	self.father.setdefault(x,x)
        if self.father[x] == x:return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]
    def union(self,x,y):
        self.father[self.find(x)] = self.find(y)
    def is_connected(self,x,y):
        return self.find(x) == self.find(y)
class Solution:
    def findRedundantConnection(self, edges):
        uf = UnionFind(len(edges))
        for x,y in edges:
            if uf.is_connected(x,y):
                return [x,y]
            uf.union(x,y)

1319. 连通网络的操作次数

class Solution:
    def makeConnected(self, n: int, connections: List[List[int]]) -> int:
        if len(connections) < n-1:return -1
        f = 
        def find(x):
            f.setdefault(x,x)
            if f[x] == x:return x
            f[x] = find(f[x])
            return f[x]
        def union(x,y):
            f[find(x)] = find(y)
        for x,y in connections:
            union(x,y)
        seen = set()
        for i in range(n):
            seen.add(find(i))
        return len(list(seen))-1

959. 由斜杠划分区域

# https://leetcode-cn.com/problems/regions-cut-by-slashes/solution/liang-chong-fang-fa-bing-cha-ji-he-dfs95-uhof/
class UnionFind:
    def __init__(self, M):
        self.father = 
        self.cnt = M
    def find(self, x):
        self.father.setdefault(x,x)
        if x != self.father[x]:
            self.father[x] = self.find(self.father[x])
        return self.father[x]
    def union(self, p, q):
        leader_p = self.find(p)
        leader_q = self.find(q)
        if leader_p == leader_q:return 
        self.father[leader_p] = leader_q
        self.cnt -= 1
class Solution:
    def regionsBySlashes(self, grid):
        n = len(grid)
        N = n * n * 4
        uf = UnionFind(N)
        def get_pos(row, col, i):
            return (row * n + col) * 4 + i
        for row in range(n):
            for col in range(n):
                v = grid[row][col] 
                if row > 0:
                    uf.union(get_pos(row - 1, col, 2), get_pos(row, col, 1))
                if col > 0:
                    uf.union(get_pos(row, col - 1, 3), get_pos(row, col, 0))
                if v == '/':
                    uf.union(get_pos(row, col, 0), get_pos(row, col, 1))
                    uf.union(get_pos(row, col, 2), get_pos(row, col, 3))
                if v == '\\\\':
                    uf.union(get_pos(row, col, 1), get_pos(row, col, 3))
                    uf.union(get_pos(row, col, 0), get_pos(row, col, 2))
                if v == ' ':
                    uf.union(get_pos(row, col, 0), get_pos(row, col, 1))
                    uf.union(get_pos(row, col, 1), get_pos(row, col, 2))
                    uf.union(get_pos(row, col, 2), get_pos(row, col, 3))

        return uf.cnt

990. 等式方程的可满足性

 class UnionFind:
        def __init__(self):
            self.parent = 
        def find(self, index):
        	self.parent.setdefault(index,index)
            if index == self.parent[index]:
                return index
            self.parent[index] = self.find(self.parent[index])
            return self.parent[index]
        def union(self, index1, index2):
            self.parent[self.find(index1)] = self.find(index2)
class Solution:
    def equationsPossible(self, equations: List[str]) -> bool:
        uf = UnionFind()
        for st in equations:
            if st[1] == "=":
                index1 = ord(st[0]) - ord("a")
                index2 = ord(st[3]) - ord("a")
                uf.union(index1, index2)
        for st in equations:
            if st[1] == "!":
                index1 = ord(st[0]) - ord("a")
                index2 = ord(st[3]) - ord("a")
                if uf.find(index1) == uf.find(index2):
                    return False
        return True

399. 除法求值

class UnionFind:
    def __init__(self):
        self.father = 
        self.value = 
    def find(self,x):
        self.father.setdefault(x,x)
        self.value.setdefault(x,1.0)
        if self.father[x] == x:return x
        root = x
        base = 1
        while self.father[root]!=root:
            root = self.father[root]
            base*= self.value[root]
        self.father[x] = root
        self.value[x] *= base
        return self.father[x]
    def union(self,x,y,val):
        root_x,root_y = self.find(x),self.find(y)
        if root_x != root_y:
            self.father[root_x] = root_y
            ##### 四边形法则更新根节点的权重
            self.value[root_x] = self.value[y] * val / self.value[x]
    def is_connected(self,x,y):
        return x in self.value and y in self.value and self.find(x) == self.find(y)
    
class Solution:
    def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
        uf = UnionFind()
        for (a,b),val in zip(equations,values):
            uf.union(a,b,val)
    
        res = [-1.0] * len(queries)

        for i,(a,b) in enumerate(queries):
            if uf.is_connected(a,b):
                res[i] = uf.value[a] / uf.value[b]
        return res

面试题 17.07. 婴儿名字

from collections import defaultdict
class UnionFind(object):
    def __init__(self):
        self.father = 
    def union(self, a, b):
        root_a = self.find(a)
        root_b = self.find(b)
        if root_a < root_b: # 字典序最小
            self.father[root_b] = root_a
        else:
            self.father[root_a] = root_b
    def find(self, x):
        self.father.setdefault(x,x)
        if self.father[x] == x:return x
        self.father[x] = self.find(self.father[x])
        return self.father[x]

class Solution(object):
    def trulyMostPopular(self, names, synonyms):
        freq_map = 
        for name_freq in names:
            name, freq_str = (part.strip().strip(')') for part in name_freq.split('('))
            freq_map[name] = int(freq_str)
        uf = UnionFind()
        for pair in synonyms:
            x,y = [i.replace(")","").replace("(","") for i in pair.split(",")]
            uf.union(x,y)
        result = []
        res_map = defaultdict(int)
        for name, freq in freq_map.items():
            res_map[uf.find(name)] += freq
        for name, freq in res_map.items():
            result.append(f"name(freq)")
        return result

547. 省份数量

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        f = 
        def find(x):
            f.setdefault(x,x)
            if f[x] == x:return x
            f[x] = find(f[x])
            return f[x]
        def union(x,y):
            f[find(x)] = find(y)
        for i in range(len(isConnected)):
            for j in range(len(isConnected[0])):
                if isConnected[i][j] == 1:
                    union(i,j)
        final_f = set()
        for i in range(len(isConnected)):
            final_f.add(find(i))
        return len(list(final_f))

765. 情侣牵手

721 账户合并

class UnionFind:
    def __init__(self):
        self.parent = 
    def union(self, index1: int, index2: int):
        self.parent[self.find(index2)] = self.find(index1)
    def find(self, index: int) -> int:
        self.parent.setdefault(index,index)
        if self.parent[index] != index:
            self.parent[index] = self.find(self.parent[index])
        return self.parent[index]
class Solution:
    def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
        emailToName = dict()
        for account in accounts:
            for email in account[1:]:
                emailToName[email以上是关于并查集-leetcode题目总结的主要内容,如果未能解决你的问题,请参考以下文章

并查集-leetcode题目总结

leetcode之并查集刷题总结1

leetcode之并查集+记忆化搜索+回溯+最小生成树刷题总结1

并查集/dfs解决——leetcode每日一题——1020飞地的数量

[leetcode] 并查集(Ⅱ)

[leetcode] 并查集(Ⅱ)