力扣547省份的数量——并查集解决

这道题我们需要用并查集进行解决,但是什么是并查集?正如他的名字一样并查集是一种数据结构。他有什么好处呐?这里他的合并,查找的时间复杂度都是只有O(1), 速度特别快,所以有时候还是需要使用并查集
并查集这三个字,一个字代表一个意思。
并(Union),代表合并
查(Find),代表查找
集(Set),代表这是一个以字典为基础的数据结构,它的基本功能是合并集合中的元素,查找集合中的元素。
并查集跟树有些类似,只不过她跟树是相反的。在树这个数据结构里面,每个节点会记录它的子节点。在并查集里,每个节点会记录它的父节点。并查集主要操作是合并与查询,它是把初始不相交的集合经过多次合并操作后合并为一个大集合,然后可以通过查询判断两个元素是否已经在同一个集合中了。
既然是一种数据结构,用python该如何进行解决呐?
首先我们先定义一个并查集的类,我们之前说了,只记录父节点,那我们应该每个里面只包含自己的父节点,

class UnionFind:
    def __init__(self):
        """
        记录每个节点的父节点
        """
        self.father = {
     }

之后我们开始他的用法的写出,首先是查找:

    def find(self, x):
        """
        查找根节点
        路径压缩
        """
        root = x

        while self.father[root] != None:
            root = self.father[root]

        # 路径压缩
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father

        return root

如果我们一个一个查找的话,如果他是这种形式的话,时间复杂度就会变成O(n)!
力扣547省份的数量——并查集解决_第1张图片
这时候我们就采用路径压缩的方法来进行解决,顾名思义,我们就是把中间的过程全部缩减为每个直接指向自己公共的祖先。
力扣547省份的数量——并查集解决_第2张图片
接下来就是合并了,合并两棵树的操作可以简单的规定让右边的树的根结点指向左边树的根结点。具体哪棵树接其实并没有太大的影响。

 def merge(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

之后啊,还有添加节点功能,节点就是祖先,所以他的爸爸没有,也就是这样


def add(self, x):
   	if x not in self.father:
       	self.father[x] = None

还有一个功能就是判断两个节点是否相连,

    def is_connected(self, x, y):
        """
        判断两节点是否相连
        """
        return self.find(x) == self.find(y)

就是看祖先是否相同。

题目解析

我们来看题目该怎么进行解决,我们可以把所有城市都设置一个初始的集合,

uf = UnionFind()
        for i in range(len(M)):
            uf.add(i)

之后我们就要开始进行判断是否连接,是一就连接,我们就把他就进行连接好

            for j in range(i):
                if M[i][j]:
                    uf.merge(i,j)

之后我们就查剩下的集合数目就好了,但是我们对集合数没有操作啊?我们可以在定义的并查集中添加一次加一个集合,连接一次减一个集合,完整代码如下:

class UnionFind:
    def __init__(self):
        self.father = {
     }
        # 额外记录集合的数量
        self.num_of_sets = 0
    
    def find(self,x):
        root = x
        
        while self.father[root] != None:
            root = self.father[root]
        
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father
        
        return root
    
    def merge(self,x,y):
        root_x,root_y = self.find(x),self.find(y)
        
        if root_x != root_y:
            self.father[root_x] = root_y
            # 集合的数量-1
            self.num_of_sets -= 1
    
    def add(self,x):
        if x not in self.father:
            self.father[x] = None
            # 集合的数量+1
            self.num_of_sets += 1

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        uf = UnionFind()
        for i in range(len(M)):
            uf.add(i)
            for j in range(i):
                if M[i][j]:
                    uf.merge(i,j)
        
        return uf.num_of_sets

你可能感兴趣的:(算法,python,python,力扣,数据结构)