这是我和yzc大佬在数据结构课上写的题,我:并查集我一点概念都没有。 yzc大佬:你做做试试。
然后我就开了这道题。我写了十分钟,妈的这个并查集怎么写,感觉只会DFS。然后yzc大佬接手,五分钟后,AC。然后我盯着他的代码看了半天没看懂。
下课后,丫的突然开窍,好简单,我可以!
题目是这样的
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
然后并查集也有两种思路,一种复杂度是恒为,另一种复杂度是,我两种都是实现了一下。
先说一下我的并查集解题思路,并查集是这样的,先寻找有关系的两个对象,每找到一组关系,然后查找两个对象分别属于的集合,若不是同一个集合,那么就合并两个集合。以此迭代。
- 线性并查集,代码如下
class Solution(object):
def findCircleNum(self, M):
"""
:type M: List[List[int]]
:rtype: int
"""
#构造初始并查集
li = [i for i in range(len(M))]
for j in range(len(M)):
for k in range(len(M)):
if M[j][k] == 1 and j != k:
#这里是寻找到了有关系的两个对象,然后开始将其中一个集合的下标的值都改成另一个集合下标的值
for num in range(len(li)):
if li[num] == min(li[j],li[k]):
li[num] = max(li[j],li[k])
return len(set(li))
- 非线性并查集
class Solution(object):
def __init__(self):
self.li = [i for i in range(205)]
def findCircleNum(self, M):
"""
:type M: List[List[int]]
:rtype: int
"""
for i in range(len(M)):
for j in range(len(M)):
if M[i][j] == 1 and i != j:
self.merge(i, j)
def merge(self, i, j):
fi = self.find(i)
fj = self.find(j)
if fi != fj:
self.li[fi] = self.li[fj]
def find(self, x):
while self.li[x] != x:
x = self.li[x]
return x
这里重点写一下非线性并查集
- 首先我们创建初始并查集
朋友圈 | 0 | 1 | 2 | 3 | … |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | … |
- 然后我们开始寻找有关系的两个同学
- 找到两个同学有关系(M[i][j]=1 and i != j)后,我们开始需要开始合并这两个朋友圈merge(),再合并朋友圈是,我们需要找到这两个朋友圈分别是几号朋友圈,这里就用到的定义的find()函数,我们使用递归的方法得到根节点(也就是他们的朋友圈序号),然后对比他们得到的朋友圈序号,将其中一个的序号进行修改。
- 最后查找我们的得到的并查集序列,查找在人数范围内总共有几个朋友圈。(查找li[i] == i的数量)。
这就是效率较高的并查集的做法。