1、题目描述:
2、题解:
参考算法第4版1.5节 union-find算法。
首先直到几个概念:触点(每个人),连接(两个人之间的关系),连通分量或者分量(朋友圈的个数)
具体见代码的注释:
方法一:quick-find
图片为算法4中的演示
数组ID为以触点为索引的数组
class Solution:
def findCircleNum(self, M: List[List[int]]) -> int:
#并查集
n = len(M)
ID = [0] * n
#初始化
for i in range(n):
ID[i] = i
#初始化全局变量:分量的个数
self.count = n
#寻找分量标识符
def find(m):
return ID[m]
#连接分量
def union(p,q):
#将p,q归并到相同的分量中
p_id = find(p)
q_id = find(q)
#如果p,q已经在相同的分量中,则忽略
if p_id == q_id:
return
#将p的分量重命名为q的名称
for i in range(len(ID)):
if ID[i] == p_id:
ID[i] = q_id
self.count -= 1
#进行处理,这时如果没有数据,是输入输出的接口,也可以把上面的函数写在外面
for i in range(n):
for j in range(i,n):
if M[i][j] == 1 : #处理知道关系的连接
if find(i) == find(j):#如果已经连通,则忽略
continue
union(i,j) #归并分量
return self.count
方法二:quick-union
图片为算法4中的演示
数组ID为以触点为索引的数组,但是有了新的定义,每个触点所对应的ID[]元素都是同一个分量中的另一个触点的名称(可能是自己)----这种联系称为链接。当且仅当分别由两个触点开始的这个过程到达了同一个根触点时,它们存在于同一个连通分量中。
class Solution:
def findCircleNum(self, M: List[List[int]]) -> int:
# 并查集 ,quick-union
n = len(M)
ID = [0] * n
# 初始化
for i in range(n):
ID[i] = i
# 初始化全局变量:分量的个数
self.count = n
# 寻找分量标识符
def find(m):
# 寻找根节点
while m != ID[m]:
m = ID[m]
return m
# 连接分量
def union(p, q):
# 将p,q归并到相同的分量中
p_id = find(p)
q_id = find(q)
# 如果p,q已经在相同的分量中,则忽略
if p_id == q_id:
return
# 将p的根触点连接到q的根触点上
ID[p_id] = q_id
self.count -= 1
# 进行处理,这时如果没有数据,是输入输出的接口,也可以把上面的函数写在外面
for i in range(n):
for j in range(i, n):
if M[i][j] == 1: # 处理知道关系的连接
if find(i) == find(j): # 如果已经连通,则忽略
continue
union(i, j) # 归并分量
return self.count
方法3:加权quick-union
与方法2相比,只需要添加一个se[]数组维护树的权值就行,这个权值是分量中的触点个数,也就是树中的节点的个数。
class Solution:
def findCircleNum(self, M: List[List[int]]) -> int:
# 并查集 ,加权quick-union
n = len(M)
ID = [0] * n
# 初始化
for i in range(n):
ID[i] = i
# 定义(由触点索引的)各个根节点所对应的分量的大小,也就是权值
sz = [1] * n
# 初始化全局变量:分量的个数
self.count = n
# 寻找分量标识符
def find(m):
# 寻找根节点
while m != ID[m]:
m = ID[m]
return m
# 连接分量
def union(p, q):
# 将p,q归并到相同的分量中
p_id = find(p)
q_id = find(q)
# 如果p,q已经在相同的分量中,则忽略
if p_id == q_id:
return
# 将小树根节点连接到大树的根节点
if sz[p_id] < sz[q_id]:
ID[p_id] = q_id
sz[q_id] += sz[p_id]
else:
ID[q_id] = p_id
sz[p_id] += sz[q_id]
self.count -= 1
# 进行处理,这时如果没有数据,是输入输出的接口,也可以把上面的函数写在外面
for i in range(n):
for j in range(i, n):
if M[i][j] == 1: # 处理知道关系的连接
if find(i) == find(j): # 如果已经连通,则忽略
continue
union(i, j) # 归并分量
return self.count