图
图的算法题通常有两种:1.深度优先或广度优先;2.并查集
1.深度优先or广度优先,通过dfs或者bfs遍历图。
2.并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。
并查集可以理解为一种解决问题的思想,将有关系的元素合并为一个集合。
并查集编写的核心通常在于find
函数或者叫getParent
并查集详解 ——图文解说,简单易懂(转)
相关题目
面试题 16.19. 水域大小
LeetCode 200. 岛屿数量
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
LeetCode 200. 岛屿数量
1.深度优先遍历,并标记数组
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
row = len(grid)
if row == 0:
return 0
col = len(grid[0])
l = [(-1,0), (0,-1), (0,1), (1,0)]
def helper(i,j):
grid[i][j] = '0'
for t in l:
x, y = i+t[0], j+t[1]
if 0<=x<row and 0<=y<col and grid[x][y]=='1':
helper(x,y)
ans = 0
for i in range(row):
for j in range(col):
if grid[i][j] == '1':
ans += 1
helper(i,j)
return ans
2.并查集
class Union:
def __init__(self, row, col):
self.parent = [i for i in range(row * col)]
self.count = 0 # 合并的个数
def union(self, i, j):
# 合并两个节点 输入按照:i < j
parent = self.parent
while parent[i] != i:
i = parent[i]
while parent[j] != j:
j = parent[j]
if i != j:
self.count += 1
if i<=j:
parent[j] = i
else:
parent[i] = j
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
row = len(grid)
if row == 0:
return 0
col = len(grid[0])
union = Union(row, col)
l = [(-1, 0), (0, -1)]
ans = 0 # 1的个数
for i in range(row):
for j in range(col):
if grid[i][j] == '1':
ans += 1
for t in l:
x, y = i + t[0], j + t[1]
if 0 <= x < row and 0 <= y < col and grid[x][y] == '1':
union.union(x * col + y, i * col + j)
return ans - union.count # 1的个数-合并的个数
面试题 17.07. 婴儿名字
每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,则 John 与 Johnny 也相同,即它们有传递和对称性。
在结果列表中,选择 字典序最小 的名字作为真实名字。
class Solution:
def trulyMostPopular(self, names: List[str], synonyms: List[str]) -> List[str]:
d = {} # name和frequency
union_d = {} # 并查集,节点为key:父节点为value
for name in names:
idx = name.find('(')
s1 = name[:idx]
num = int(name[idx+1:-1])
d[s1] = num
for s in synonyms:
idx = s.find(',')
s1 = s[1:idx]
s2 = s[idx+1:-1]
while s1 in union_d:
s1 = union_d[s1]
while s2 in union_d:
s2 = union_d[s2]
if s1 != s2:
s_min, s_max = min(s1, s2), max(s1, s2)
union_d[s_max] = s_min
d[s_min] = d.get(s_max, 0) + d.get(s_min, 0)
if s_max in d:
del d[s_max]
ans = [k + '(' + str(v) + ')' for k,v in d.items()]
return ans
LeetCode 399. 除法求值
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。
返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。
注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
注意:未在等式列表中出现的变量是未定义的,因此无法确定它们的答案。
LeetCode 399. 除法求值
class Solution {
private:
class Union{
public:
vector<int> parent;
vector<double> w;
Union(int n){
parent = vector<int>(n,0);
w = vector<double>(n,1.0);
for(int i=0;i<n;++i){
parent[i] = i;//初始化每个元素的parent为自己
}
}
int getParent(int x){
if(x!=parent[x]){
int t = parent[x];
parent[x] = getParent(t);
//1.未遍历过需要相乘 以a/b b/c为例 需要更新a/c,这里只需更新a/b,因为b/c在递归中已经更新过 2.已更新过,c的父节点还是c,所以结果不变
w[x] = w[x]*w[t];
}
return parent[x];
}
//两者的根不同则进行合并
void add(int a, int b, double val){
int pa = getParent(a);
int pb = getParent(b);
if(pa!=pb){
parent[pa] = pb;
w[pa] = w[b]/w[a]*val;
}
}
double getW(int a, int b){
if(getParent(a) == getParent(b)){
return w[a]/w[b];
}
return -1.0;
}
};
public:
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
unordered_map<string, int> ump;
int n = (int)equations.size();
for (int i = 0; i < n; ++i) {
if (!ump.count(equations[i][0])) ump.insert({ equations[i][0],ump.size() });
if (!ump.count(equations[i][1])) ump.insert({ equations[i][1],ump.size() });
}
int m = (int)ump.size();
Union un = Union(m);
for(int i=0;i<n;++i){
un.add(ump[equations[i][0]], ump[equations[i][1]], values[i]);
}
vector<double> res;
for(auto& v: queries){
if(ump.count(v[0]) && ump.count(v[1])){
res.emplace_back(un.getW(ump[v[0]], ump[v[1]]));
}else res.emplace_back(-1.0);
}
return res;
}
};