今天接了字节数据开发实习的一面,面试官问了我一个类似于计算无向图的连通分量数量的代码题,面试官引导我往图方面想,当时没撕出来,还是对图算法不熟悉!
想的是通过DFS遍历,需要遍历几次DFS能够遍历所有的节点,那么就有几个连通分量。
后面在网上又搜集到了一种新的解题思路,所以在这里记录学习一下。
题目:
一下2维列表中[0, 1]表示 0关注1 ,【3, 2】表示3关注2,【3, 4】表示3关注4,那么【3,2,4】应该属于一个社交圈,现有以下2维列表,求出共有多少个社交圈(连通图)
[[0, 1],
[3,2],
[2,3],
[4, 5],
[3, 4],
[3,5]]
算法思想:
class UnionFindSet(object):
def __init__(self, m):
# m, n = len(grid), len(grid[0])
self.roots = [i for i in range(m)]
# rank用来干啥?? 一种用于并查集的秩优化 的数据结构 初始化成0
self.rank = [0 for i in range(m)]
self.count = m #初始化成m个连通分量
def find(self, member):
# 找到父节点
# 父节点定义:该集团的头头
tmp = []# 存储member向父节点追溯的链式关系节点
while member != self.roots[member]: #通过遍历链式关系 找到父节点 并且用tmp列表存起来链式关系的节点 直到 member节点为最终的父节点为止
# 添加到tmp中
tmp.append(member)
member = self.roots[member] # 继续寻找链式关系 直到 member节点为最终的父节点为止
for root in tmp:# 更新链式关系中的每个节点为最后的父节点
self.roots[root] = member
return member # 返回最后的父节点
def union(self, p, q): # 合并两个集团
parentP = self.find(p) # 找到p的父节点
parentQ = self.find(q) # 找到Q的父节点
if parentP != parentQ: # 如果父节点不同 那就合并两个集合
if self.rank[parentP] > self.rank[parentQ]: # p父节点 0 q父节点 -1
self.roots[parentQ] = parentP # 把q的父节点设成p
elif self.rank[parentP] < self.rank[parentQ]: # p父节点-1 q父节点 0
self.roots[parentP] = parentQ # 把p的父节点设成q
else: # p父节点 0 q父节点 0 或者 p父节点 -1 q父节点 -1
self.roots[parentQ] = parentP # 默认q的父节点设成p的父节点
self.rank[parentP] -= 1 # p的父节点的rank设为-1
self.count -= 1 # 最终的连通分量-1
class Solution(object):
def countComponents(self, n, edges):
"""
:type n: int
:type edges: List[List[int]]
:rtype: int
"""
# 定义好需要用的数据结构
# 初始化成n个连通分量 通过添加边 合并连通分量集合 更新连通分量的父节点
ufs = UnionFindSet(n)
for edge in edges:
# 遍历边 不断的合并
start, end = edge[0], edge[1]
ufs.union(start, end)
return ufs.count # 返回最终的连通分量数
n = 6
edges = [[0, 1],[3,2],[2,3],[4, 5],[3, 4],[3,5]]
so = Solution()
num = so.countComponents(n, edges)
print(num)