Open azl397985856 opened 2 years ago
这个题是并查集的模板题。
题目实际上问的是: 有多少个连通块。
并查集一般的时间复杂度是O(1)的, 但要做到O(1)需要同时实现:
理论上来讲, 只写路径压缩时的时间复杂度是O(log N), 但实际上的效果基本上可以看成O(1)的。
实现语言: C++
class Solution {
vector<int> p; // p(parents): 存储每个集合的父节点
public:
int findCircleNum(vector<vector<int>>& M) {
const int n = M.size();
for (int i = 0; i < n; i++)
p.push_back(i);
int count = n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (M[i][j] == 1 && find(i) != find(j)) /* i和j当前不在同一个集合, 但发现它们之间有边, 就可以合并了 */
{
p[find(i)] = find(j);
count--;
}
}
}
return count;
}
int find(int x)
{
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
};
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
C++ Code:
class UF
{
vector<int> parent;
int count;
public:
UF(int n)
{
for(int i=0; i< n; i++)
parent.push_back(i);
count = n;
}
void uninTowNode(int i, int j)
{
int iParent = findParent(i);
int jParent = findParent(j);
if(iParent!=jParent)
{
parent[iParent] = jParent;
count--;
}
}
int findParent(int i)
{
while(parent[i]!=i)
{
parent[i] = parent[parent[i]]; // compress
i=parent[i];
}
return i;
}
int component()
{
return count;
}
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
int nodeNum = isConnected.size();
UF uf(nodeNum);
for(int i=0; i< nodeNum; i++)
{
for(int j=i+1; j< nodeNum; j++)
{
if(isConnected[i][j])
{
uf.uninTowNode(i,j);
}
}
}
return uf.component();
}
};
冰茶几
class UF:
def __init__(self):
self.parent = {}
self.size = defaultdict(lambda: 1)
self.count = 0
def add(self, x):
if x not in self.parent:
self.parent[x] = x
self.count += 1
def find(self, x):
self.parent.setdefault(x, x)
ox = x
while x != self.parent[x]:
x = self.parent[x]
self.parent[ox] = x
return x
def merge(self, x, y):
px, py = self.find(x), self.find(y)
if px == py: return
if self.size[px] < self.size[py]:
self.size[py] += self.size[px]
self.parent[px] = py
else:
self.size[px] += self.size[py]
self.parent[py] = px
self.count -= 1
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
M, N = len(isConnected), len(isConnected[0])
uf = UF()
for x in range(M):
for y in range(N):
if isConnected[x][y] == 1:
uf.add(x)
uf.add(y)
uf.merge(x, y)
return uf.count
C++ Code:
class Solution {
vector<int> parent;
public:
int find(int x){
if(parent[x]!=x)
parent[x] = find(parent[x]);
return parent[x];
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
for(int i=0;i<n;i++)
parent.push_back(i);
int res = n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(isConnected[i][j]==1&&find(i)!=find(j))
{
parent[find(i)] = j;
res--;
}
}
return res;
}
};
通过union find里面找连通图的个数
class UF:
def __init__(self, n) -> None:
self.parent = {i: i for i in range(n)}
self.size = n
def find(self, i):
if self.parent[i] != i:
self.parent[i] = self.find(self.parent[i])
return self.parent[i]
def connect(self, i, j):
root_i, root_j = self.find(i), self.find(j)
if root_i != root_j:
self.size -= 1
self.parent[root_i] = root_j
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
uf = UF(n)
for i in range(n):
for j in range(n):
if isConnected[i][j]:
uf.connect(i, j)
return uf.size
Time O(n^2)
Space O(n)
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
self.isConnected = isConnected
self.n = len(isConnected)
self.visited = [False] * self.n
provinces = 0
for i in range(self.n):
if self.visited[i] == False:
self.visited[i] = True
self.dfs(i)
provinces += 1
continue
return provinces
def dfs(self, i):
for j in range(self.n):
if i == j:
continue
if self.visited[j] == True:
continue
if self.isConnected[i][j] == 0:
continue
self.visited[j] = True
self.dfs(j)
class UnionFind:
def __init__(self, size):
self.parent = [i for i in range(size)]
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
root_x = self.find(x)
root_y = self.find(y)
if root_x == root_y:
return
self.parent[root_x] = root_y
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
if not isConnected:
return 0
n = len(isConnected)
union_find = UnionFind(n)
for i in range(n):
for j in range(n):
if isConnected[i][j]:
union_find.union(i, j)
result = 0
for i in range(n):
if union_find.find(i) == i:
result += 1
return result
BFS 要注意即时标visited,防止环导致的重复遍历发生。
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
visited=[False]*len(isConnected)
res=0
for x,i in enumerate(isConnected):
if not visited[x]:
visited[x]=True
res+=1
connected=[x]
# for y,j in enumerate(i):
# if j and x!=y:
# connected.append(y)
# visited[y]=True
for j in connected:
# visited[j]=True
for y,k in enumerate(isConnected[j]):
if k and not visited[y] and j!=y:
connected.append(y)
visited[y]=True
return res
Idea find all the neighbors of each island then go through each island, count how many groups for all island based on dictionary
Time: (n^2)
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
ans = 0
dic = {i:[] for i in range(len(isConnected))}
for i in range(len(isConnected)):
for j in range(len(isConnected)):
if isConnected[i][j] == 1 and i!=j:
dic[i].append(j)
sets = []
for i in range(len(isConnected)):
if i not in sets:
sets.append(i)
if not dic[i]:
ans +=1
else:
queue = [i]
while queue:
t = queue.pop()
if dic[t]:
for j in dic[t]:
if j not in sets:
queue.append(j)
sets.append(j)
ans +=1
return ans
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
parent = {}
def find(i):
parent.setdefault(i, i)
if i != parent[i]:
parent[i] = find(parent[i])
return parent[i]
def union(i, j):
parent[find(i)] = find(j)
n = len(isConnected)
for i in range(n):
for j in range(i+1, n):
if isConnected[i][j] == 1:
union(i, j)
return len(set([find(i) for i in range(n)]))
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
UF uf = new UF(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (isConnected[i][j] == 1) {
uf.union(i, j);
}
}
}
return uf.size;
}
private static class UF {
int[] parent;
int size;
public UF(int n) {
parent = new int[n];
size = n;
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public int find(int x) {
while (parent[x] != x) {
x = parent[x];
}
return x;
}
public void union(int x, int y) {
if (find(x) != find(y)) {
parent[find(x)] = find(y);
size--;
}
}
}
}
Union Find
class UF:
def __init__(self, n):
self.p = [i for i in range(n)]
self.components = n
def union(self, i, j):
pi = self.par(i)
pj = self.par(j)
if pi != pj:
self.components -= 1
self.p[pi] = pj
def par(self, i):
tmp = i
while self.p[i] != i:
i = self.p[i]
j = tmp
# compression
while self.p[j] != j:
t = self.p[j]
self.p[j] = i
j = t
return i
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
uf = UF(n)
for i in range(n):
for j in range(n):
if isConnected[i][j]:
uf.union(i, j)
return uf.components
union find
class Solution {
public int findCircleNum(int[][] M) {
DSU dsu = new DSU(M.length);
for (int i = 0; i < M.length; ++i) {
for (int j = i; j < M[i].length; ++j)
if (M[i][j] == 1) dsu.union(i, j);
}
return dsu.getNumFrdCir();
}
}
class DSU {
private int[] par = null, rnk = null;
private int numFrdCir = 0;
public DSU(int len) {
this.numFrdCir = len;
this.par = new int[len];
this.rnk = new int[len];
for (int i = 0; i < len; ++i)
this.par[i] = i;
}
public int find(int x) {
if (this.par[x] != x)
this.par[x] = this.find(this.par[x]);
return this.par[x];
}
public void union(int x, int y) {
int xr = this.find(x),
yr = this.find(y);
if (xr == yr)
return;
else if (this.rnk[xr] < this.rnk[yr])
this.par[xr] = yr;
else if (this.rnk[xr] > this.rnk[yr])
this.par[yr] = xr;
else {
this.par[yr] = xr;
this.rnk[xr]++;
}
this.numFrdCir--;
}
public int getNumFrdCir() {
return this.numFrdCir;
}
}
# union find with path compression and union by rank
# time: (n^3), n is the lenght of isConnected
# space: O(n)
class Union_find:
def __init__(self, n):
self.count = n
self.parent = {}
self.size = {}
for i in range(n):
self.parent[i] = i
self.size[i] = 1
def find(self, node):
if self.parent[node] != node:
parent = self.parent[node]
# path compression
self.parent[node] = parent
return self.find(parent)
return node
def union(self, p, q):
if self.connected(p, q): return
self.count -= 1
p_parent = self.find(p)
q_parent = self.find(q)
# put smaller union to become part of a bigger union
if self.size[p_parent] < self.size[q_parent]:
self.parent[p_parent] = q_parent
self.size[q_parent] += self.size[p_parent]
else:
self.parent[q_parent] = p_parent
self.size[p_parent] += self.size[q_parent]
def connected(self, p, q):
return self.find(p) == self.find(q)
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
union_find = Union_find(n)
for i in range(n):
for j in range(n):
if i == j: continue
if isConnected[i][j] == 1:
union_find.union(i, j)
return union_find.count
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
def find(i):
# find the root of i
while root[i] != i:
root[i] = root[root[i]]
i = root[i]
return i
def union(i, j):
nonlocal count
root_i, root_j = find(i), find(j)
if root_i != root_j:
root[root_i] = root_j
count -= 1
n = len(isConnected)
count = n
root = [i for i in range(n)]
for i in range(n):
for j in range(i + 1, n):
if isConnected[i][j]:
union(i, j)
return count
Time complexity: O(N^2)
Space complexity: O(N)
var findCircleNum = function(isConnected) {
const n = isConnected.length;
if (n==1){return 1};
let visited = new Set();
var visitedNeighbors = index => {
visited.add(index);
for (let j=0; j<n; j++){
if (isConnected[index][j] === 1 && index!=j && visited.has(j)===false){
visitedNeighbors(j);
};
};
};
let result = 0;
for (let i=0; i<n; i++){
if (visited.has(i) === false){
result++;
visitedNeighbors(i);
};
};
return result;
};
class Solution:
def findCircleNum(self, isConnected):
n = len(isConnected)
if n == 1:
return 1
adj_matrix = isConnected
visited = set()
def visitProvCities(city_idx):
visited.add(city_idx)
for neighbor_city, directly_connected in enumerate(adj_matrix[city_idx]):
if directly_connected and (neighbor_city not in visited):
visitProvCities(neighbor_city)
return
province_count = 0
for city_idx in range(n):
if city_idx not in visited:
province_count += 1
visitProvCities(city_idx)
return province_count
思路 并查集
func findCircleNum(isConnected [][]int) int {
out := 0
n := len(isConnected)
parent := make([]int,n)
for i:= range parent{
parent[i] = i
}
var find func(int)int
find = func(x int) int{
if parent[x] != x{
parent[x] = find(parent[x])
}
return parent[x]
}
union := func(from,to int){
parent[find(from)] = find(to)
}
for i,row := range isConnected{
for j:=i+1;j<n;j++{
if row[j] == 1{
union(i,j)
}
}
}
for i,p := range parent{
if i == p{
out++
}
}
return out
}
时间复杂度O(n²logN) 空间复杂度:O(N)
DFS
JavaScript Code
var findCircleNum = function (M) {
const visited = Array.from({ length: M.length }).fill(0);
let res = 0;
for (let i = 0; i < visited.length; i++) {
if (!visited[i]) {
visited[i] = 1;
dfs(i);
res++;
}
}
return res;
function dfs(i) {
for (let j = 0; j < M.length; j++) {
if (i !== j && !visited[j] && M[i][j]) {
visited[j] = 1;
dfs(j);
}
}
}
};
复杂度
class Solution {
public:
vector<int> p;
int find(int x) {
if (p[x] != x) {
p[x] = find(p[x]);
}
return p[x];
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
for (int i = 0; i < n; i++) {
p.push_back(i);
}
int count = n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (isConnected[i][j] && find(i) != find(j)) {
count --;
// 找到不同集的两个,计算数量后,给他们合到一个集合里面去
p[find(i)] = find(j);
}
}
}
return count;
}
};
Union-find
class UF:
def __init__(self, M):
self.parent = {}
self.cnt = M
self.size = {}
for i in range(M):
self.parent[i] = i
self.size[i] = 1
def find(self,x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def connected(self, p, q):
return self.parent[p] == self.parent[q]
def union(self, i, j):
root_i, root_j = self.find(i), self.find(j)
if root_i != root_j:
self.parent[root_i] = root_j
self.size[root_i] += self.size[root_j]
self.cnt -= 1
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
uf = UF(n)
for i in range(n):
for j in range(i, n) :
if isConnected[i][j] and i != j:
uf.union(i, j)
return uf.cnt
Time : O(N^2) Space : O(N)
Java Code:
class Solution {
//并查集
public static int findCircleNum(int[][] isConnected) {
int result = 0;
int[] parent = new int[isConnected.length];
Arrays.fill(parent,-1);
//当前节点的深度
int[] rank = new int[isConnected.length];
int n = isConnected.length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if(i !=j && isConnected[i][j] == 1){
union(parent,rank,i,j);
}
}
}
//统计-1的个数
for (int p : parent) {
if(p == -1){
result++;
}
}
return result;
}
static int find(int[] parent,int x){
int rootVal = x;
while (parent[rootVal] != -1){
rootVal = parent[rootVal];
}
return rootVal;
}
static void union(int[] parent,int[] rank,int x,int y){
if(x == y){
return;
}
int xRoot = find(parent, x);
int yRoot = find(parent, y);
//根节点不是同一个 处理 是同一个根节点 证明不需要处理
if(xRoot != yRoot){
if(rank[xRoot] > rank[yRoot] ){
parent[yRoot] = xRoot;
}else if(rank[yRoot] > rank[xRoot]){
parent[xRoot] = yRoot;
}else {
parent[xRoot] = yRoot;
rank[yRoot]++;
}
}
}
}
思路:DFS
class Solution {
public int findCircleNum(int[][] isConnected) {
//城市数量
int n = isConnected.length;
//表示哪些城市被访问过
boolean[] visited = new boolean[n];
int count = 0;
//遍历所有的城市
for(int i = 0; i < n; i++){
//如果当前城市没有被访问过,说明是一个新的省份,
//count要加1,并且和这个城市相连的都标记为已访问过,
//也就是同一省份的
if(!visited[i]){
dfs(isConnected, visited, i);
count++;
}
}
return count;
}
public void dfs(int[][] isConnected, boolean[] visited, int i){
for(int j = 0; j < isConnected.length; j++){
if(isConnected[i][j] == 1 && !visited[j]){
//如果第i和第j个城市相连,说明他们是同一个省份的,把它标记为已访问过
visited[j] = true;
//然后继续查找和第j个城市相连的城市
dfs(isConnected, visited, j);
}
}
}
}
时间复杂度: O(N^2) 空间复杂度: O(N)
basically the question is to find out the number of connected components in the graph. Use union-find
Time: O(n^2) Space: O(n)
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
UnionFind UF = new UnionFind(n);
for(int i=0;i<n;i++) {
for(int j=i+1;j<n;j++) {
if(isConnected[i][j]==1) {
UF.union(i,j);
}
}
}
return UF.count();
}
class UnionFind {
private int count;
private int[] parent;
private int[] size;
public UnionFind(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ)
return;
if (size[rootP] > size[rootQ]) {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
} else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
count--;
}
private int find(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public int count() {
return count;
}
}
}
并查集模板
class Solution {
public int findCircleNum(int[][] isConnected) {
int count = isConnected.length;
Map<Integer, Integer> parents = new HashMap<>();
for (int i=0; i<isConnected.length; i++) {
parents.put(i, null);
for (int j=0; j<i; j++) {
if (isConnected[i][j] == 1) {
// union
int parentOfI = find(parents, i);
int parentOfJ = find(parents, j);
if (parentOfI != parentOfJ) {
parents.put(parentOfI, parentOfJ);
count--;
}
}
}
}
return count;
}
private int find(Map<Integer, Integer> parents, int x) {
int root = x;
while (parents.get(root) != null) {
root = parents.get(root);
}
// compress
while (x != root) {
int oldParent = parents.get(x);
parents.put(x, root);
x = oldParent;
}
return root;
}
}
TC: O(n^2 * logn)
SC: O(n)
面试里暂时不会考并查集,所以练练DFS
深度优先遍历结点,如果已经访问就跳过,最外层记录执行dfs的次数代表province数。
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
def dfs(i):
visited.add(i)
for j in range(len(isConnected[i])):
if j not in visited and isConnected[i][j]==1:
dfs(j)
visited = set()
provinces = 0
for i in range(len(isConnected)):
if i not in visited:
dfs(i)
provinces +=1
return provinces
时间复杂度: O(n^2)
空间复杂度: O(n)
day【74】题目地址(547. 省份数量)
https://leetcode-cn.com/problems/number-of-provinces/
并查集求联通分量的个数,合并过程中记录 BFS和DFS均可
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
def find(x):
if x != parent[x]:
parent[x] = find(parent[x])
return parent[x]
M = len(isConnected)
parent = list(range(M))
for i in range(M):
for j in range(i+1,M):
if isConnected[i][j]:
parent[find(i)] = find(j)
COUNT = sum(parent[i] == i for i in range(M))
return COUNT
时间复杂度:O(n^2logn) 需要便利isConnected 所有元素,时间复杂度为O(N^2) 相连关系需要2次查找最多一次合并
空间复杂度:O(n)记录每个城市所属联通的祖先
并查集和DFS都试了试,很久没复习并查集了,正好复习下概念
并查集的思路就是遍历图,连通就合并,最后算有几个连通的即可
DFS就是去遍历所有没有访问过且连通的点,遍历过就visited设为True,遍历到底了就加1
class Solution:
def findCircleNum1(self, isConnected: List[List[int]]) -> int:
# 第一次尝试并查集
n = len(isConnected)
d = {}
for i in range(n):
d[i] = i
for i in range(n):
for j in range(i + 1, n):
l, r = i, j
if isConnected[l][r] == 1:
while d[l] != l:
l = d[l]
while d[r] != r:
r = d[r]
d[r] = l
return len([x for x in d.items() if x[0] == x[1]])
def findCircleNum2(self, isConnected: List[List[int]]) -> int:
# dfs写法
n = len(isConnected)
visited = set()
ans = 0
def dfs(i):
for j in range(n):
if isConnected[i][j] == 1 and j not in visited:
visited.add(j)
dfs(j)
for i in range(n):
if i not in visited:
dfs(i)
ans += 1
return ans
def findCircleNum3(self, isConnected: List[List[int]]) -> int:
# 用板子的写法
n = len(isConnected)
uf = UF(n)
for i in range(n):
for j in range(i + 1, n):
if isConnected[i][j] == 1:
uf.union(i, j)
return uf.cnt
def findCircleNum4(self, isConnected: List[List[int]]) -> int:
# 了解板子后再尝试
n = len(isConnected)
ans = n
parent = {}
for i in range(n):
parent[i] = i
for i in range(n):
for j in range(i + 1, n):
if isConnected[i][j] == 1:
p, q = i, j
while p != parent[p]:
p = parent[p]
while q != parent[q]:
q = parent[q]
if p != q:
parent[q] = p
ans -= 1
return ans
class UF:
def __init__(self, n):
self.parent = {}
self.cnt = 0
for i in range(n):
self.parent[i] = i
self.cnt += 1
def find(self, x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
p_parent = self.find(p)
q_parent = self.find(q)
if p_parent == q_parent:
return
self.parent[q_parent] = p_parent
self.cnt -= 1
并查集
class Solution {
private:
vector<int> parent;
int find(int n){
while(parent[n]!=n)
n=parent[n];
return n;
}
void Union(int n,int m)
{
parent[find(n)]=find(m);
}
public:
int findCircleNum(vector<vector<int>>& isConnected) {
int n=isConnected.size();
parent=vector<int>(n,0);
for(int i=0;i<n;i++)
parent[i]=i;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(isConnected[i][j]){
Union(i,j);
}
}
};
int ans=0;
for(int i=0;i<n;i++){
if(parent[i]==i){
ans++;
}
}
return ans;
}
};
复杂度分析
UF
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
self.count = len(isConnected)
parents = [i for i in range(self.count)]
def find(x):
if x != parents[x]:
parents[x] = find(parents[x])
return parents[x]
return x
def union(x,y):
if find(x) == find(y): return
parents[parents[x]] = parents[y]
self.count-=1
for i in range(len(isConnected)):
for j in range(i+1, len(isConnected)):
if isConnected[i][j]:
union(i,j)
return self.count
Space: O(nnlogn) Time: O(n)
使用并查集的思路
遍历每个城市 i, 再遍历每个城市 j, 如果 i 和 j 是 connected 的, 并且 不在一个联通分量中, 那么 uf.connect(i, j), 将 i 和 j 添加到 同一个联通分量中
最后返回 联通分量的个数
class UF:
def __init__(self, num):
self.parents = [i for i in range(num)]
self.num = num
def find(self, i):
while self.parents[i] != i:
# path compression
new_parent = self.parents[self.parents[i]]
self.parents[i] == new_parent
i = new_parent
return i
def union(self, i, j):
if self.find(i) != self.find(j):
p_i, p_j = self.find(i), self.find(j)
self.parents[p_i] = p_j
self.num -= 1
def connected(self, i, j):
return self.find(i) == self.find(j)
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
uf = UF(n)
for i in range(n):
for j, val in enumerate(isConnected[i]):
if val == 1 and not uf.connected(i, j):
uf.union(i,j)
return uf.num
时间复杂度: O(n^2 logn) 需要遍历矩阵中所有元素, 复杂度为 O(n^2), 如果遇到相连关系,则需要进行 2 次查找和最多 1 次合并,一共需要进行 2n^2 次查找和最多 n^2 次合并,因此总时间复杂度是 O(2n^2 logn^2)=O(n^2 log n) 这里的并查集使用了路径压缩,但是没有使用按秩合并,最坏情况下的时间复杂度是 O(n^2 log n)
空间复杂度: O(n) 并查集的空间复杂度
class Solution:
def findRoot(self, root, i):
if root[i] != i:
root[i] = self.findRoot(root, root[i])
return root[i]
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
root = list()
for i in range(n):
root.append(i)
count = n
for i in range(n):
for j in range(n):
if isConnected[i][j] == 1:
root1, root2 = self.findRoot(root, i), self.findRoot(root, j)
if root1 != root2:
root[root2] = root1
count -= 1
return count
Time Complexity: O(n^2 * logn), Space Complexity: O(n)
class UnionFind {
private Map<Integer, Integer> father;
private int numOfSets;
public UnionFind() {
father = new HashMap<Integer, Integer>();
numOfSets = 0;
}
public void add(int x) {
if(!father.containsKey(x)) {
father.put(x, null);
numOfSets++;
}
}
public void merge(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if(rootX != rootY) {
father.put(rootX, rootY);
numOfSets--;
}
}
public int find(int x) {
int root = x;
while(father.get(root) != null) {
root = father.get(root);
}
while(x != root) {
int originalFather = father.get(x);
father.put(x, root);
x = originalFather;
}
return root;
}
public boolean isConnected(int x, int y) {
return find(x) == find(y);
}
public int getNumOfSets() {
return numOfSets;
}
}
class Solution {
public int findCircleNum(int[][] isConnected) {
UnionFind uf = new UnionFind();
for(int i = 0; i < isConnected.length; i++) {
uf.add(i);
for(int j = 0; j < i; j++) {
if(isConnected[i][j] == 1) {
uf.merge(i, j);
}
}
}
return uf.getNumOfSets();
}
}
O(n^2 * logn)
O(n)
bfs
class Solution {
public int findCircleNum(int[][] isConnected) {
int numCities = isConnected.length;
boolean[] visited = new boolean[provinces];
int numProvinces = 0;
Queue<Integer> queue = new LinkedList<Integer>();
for (int i = 0; i < numCities; i++) {
if (!visited[i]) {
queue.offer(i);
while (!queue.isEmpty()) {
int j = queue.poll();
visited[j] = true;
for (int k = 0; k < numCities; k++) {
if (isConnected[j][k] == 1 && !visited[k]) {
queue.offer(k);
}
}
}
numProvinces++;
}
}
return numProvinces;
}
}
时间: O(n^2) \ 空间: O(n)
并查集解,图论解似乎也可以
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
uf = UF(n)
for i in range(n):
for j in range(i+1, n):
if isConnected[i][j] == 1: uf.union(i,j)
return uf.cnt
class UF:
def __init__(self, M):
self.parent = {}
self.size = {}
self.cnt = 0
# 初始化 parent,size 和 cnt
# size 是一个哈希表,记录每一个联通域的大小,其中 key 是联通域的根,value 是联通域的大小
# cnt 是整数,表示一共有多少个联通域
for i in range(M):
self.parent[i] = i
self.cnt += 1
self.size[i] = 1
def find(self, x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
if self.connected(p, q): return
# 小的树挂到大的树上, 使树尽量平衡
leader_p = self.find(p)
leader_q = self.find(q)
if self.size[leader_p] < self.size[leader_q]:
self.parent[leader_p] = leader_q
self.size[leader_q] += self.size[leader_p]
else:
self.parent[leader_q] = leader_p
self.size[leader_p] += self.size[leader_q]
self.cnt -= 1
def connected(self, p, q):
return self.find(p) == self.find(q)
class Solution {
public:
int circle[210];
void unionInit() {
for (int i = 0; i < 210; i++) {
circle[i] = i;
}
}
void merge(int x, int y) {
circle[get(x)] = circle[get(y)];
}
int get(int x) {
if (circle[x] == x) return x;
circle[x] = get(circle[x]);
return circle[x];
}
int findCircleNum(vector<vector<int>>& M) {
unionInit();
for (int i = 0; i < M.size(); i++) {
for (int j = 0; j < M[0].size(); j++) {
if (M[i][j] == 1 && i!= j) {
merge(i,j);
}
}
}
int count = 0;
for (int i = 0; i < M.size(); i++) {
if (circle[i] == i) count++;
}
return count;
}
};
套模板,相连的城市就union,最后算一下cnt就可以
class UnionFind:
def __init__(self, n):
self.parent = dict()
self.size = dict()
self.cnt = 0
for i in range(n):
self.parent[i] = i
self.size[i] = 1
self.cnt += 1
def find(self, x):
while x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
if self.find(p) == self.find(q):
return
parent_p, parent_q = self.find(p), self.find(q)
if self.size[parent_p] > self.size[parent_q]:
self.parent[parent_q] = parent_p
self.size[parent_p] += self.size[parent_q]
else:
self.parent[parent_p] = parent_q
self.size[parent_q] += self.size[parent_p]
self.cnt -= 1
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
uf = UnionFind(len(isConnected))
for i in range(len(isConnected)):
for j in range(len(isConnected[i])):
if isConnected[i][j] == 1:
uf.union(i, j)
return uf.cnt
用visited记录是否访问,没访问过的每个节点都dfs到底
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
visited = set()
res = 0
def dfs(i):
# dfs只标记visited
# i所有dfs遍历到的邻居,都标记为visited
visited.add(i)
for j, is_neighbor in enumerate(isConnected[i]):
if is_neighbor == 1 and j not in visited:
dfs(j)
for i in range(n):
if i not in visited:
dfs(i)
res += 1
return res
用visited记录是否访问,没访问过的每个节点都bfs到底
from collections import deque
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
n = len(isConnected)
visited = set()
res = 0
def bfs(i):
queue = deque([i])
while queue:
cur = queue.popleft()
visited.add(cur)
for j in range(n):
if isConnected[cur][j] == 1 and j not in visited:
queue.append(j)
for i in range(n):
if i not in visited:
bfs(i)
res += 1
return res
https://leetcode.com/problems/number-of-provinces/submissions/
-union find
union find with size。
class UnionFind:
def __init__(self, n):
self.parent = {i:i for i in range(n)}
self.count = n
def find(self, x):
while self.parent[x] != x:
x = self.parent[x]
return x
def union(self, x, y):
x_parent = self.find(x)
y_parent = self.find(y)
if x_parent != y_parent:
self.parent[x_parent] = y_parent
self.count -= 1
def get_cnt(self):
return self.count
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
if not isConnected or len(isConnected) == 0:
return 0
n = len(isConnected)
uf = UnionFind(n)
for i in range(n):
for j in range(n):
if isConnected[i][j] == 1:
uf.union(i, j)
return uf.get_cnt()
时间复杂度: O(N^2) 空间复杂度:O(N)
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
UnionFind UF = new UnionFind(n);
for(int i=0;i<n;i++) {
for(int j=i+1;j<n;j++) {
if(isConnected[i][j]==1) {
UF.union(i,j);
}
}
}
return UF.count();
}
class UnionFind {
private int count;
private int[] parent;
private int[] size;
public UnionFind(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ)
return;
if (size[rootP] > size[rootQ]) {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
} else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
count--;
}
private int find(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public int count() {
return count;
}
}
}
并查集。此题为集合的合并与查询问题,适合用并查集。省份数量就是连通域数量。
class Solution {
public int findCircleNum(int[][] isConnected) {
int provinces = isConnected.length;
int[] parent = new int[provinces];
// initialize all the nodes, treat them as roots
for (int i = 0; i < provinces; i++) {
parent[i] = i;
}
// check 2D array and connect nodes
for (int i = 0; i < provinces; i++) {
for (int j = i + 1; j < provinces; j++) {
if (isConnected[i][j] == 1) {
union(parent, i, j);
}
}
}
int circles = 0;
for (int i = 0; i < provinces; i++) {
// check the number of roots
if (parent[i] == i) {
circles++;
}
}
return circles;
}
public void union(int[] parent, int i1, int i2) {
parent[find(parent, i1)] = find(parent, i2);
}
public int find(int[] parent, int i) {
if (parent[i] != i) {
parent[i] = find(parent, parent[i]);
}
return parent[i];
}
}
复杂度分析
并查集
class Solution {
public int findCircleNum(int[][] isConnected) {
int provinces = isConnected.length;
int[] parent = new int[provinces];
for (int i = 0; i < provinces; i++) {
parent[i] = i;
}
for (int i = 0; i < provinces; i++) {
for (int j = i + 1; j < provinces; j++) {
if (isConnected[i][j] == 1) {
union(parent, i, j);
}
}
}
int circles = 0;
for (int i = 0; i < provinces; i++) {
if (parent[i] == i) {
circles++;
}
}
return circles;
}
public void union(int[] parent, int index1, int index2) {
parent[find(parent, index1)] = find(parent, index2);
}
public int find(int[] parent, int index) {
if (parent[index] != index) {
parent[index] = find(parent, parent[index]);
}
return parent[index];
}
}
public class Solution {
public void dfs(int[][] M, int[] visited, int i) {
for (int j = 0; j < M.length; j++) {
if (M[i][j] == 1 && visited[j] == 0) {
visited[j] = 1;
dfs(M, visited, j);
}
}
}
public int findCircleNum(int[][] M) {
int[] visited = new int[M.length];
int count = 0;
for (int i = 0; i < M.length; i++) {
if (visited[i] == 0) {
dfs(M, visited, i);
count++;
}
}
return count;
}
}
class Solution {
// 注意这题虽然是二位矩阵,但是只有n个节点,节点是一维的不是二维
public int findCircleNum(int[][] isConnected) {
// dfs to find all the connected cities
// use outer forloop to count the num, everytime we finish one round of dfs, we go to the for loop
// and start with the entry point of a new group, do counter++
// needed objects
// boolean visited[][]
// directions[][]
// isValid()
int n = isConnected.length;
if (n < 1) {
return 0;
}
int counter = 0;
Set<Integer> visited = new HashSet<>();
for (int i = 0; i < n; i++) {
if (!visited.contains(i)) {
counter++;
dfs(isConnected, i, visited);
}
}
return counter;
}
private void dfs (int[][] matrix, int i, Set<Integer> visited) {
int n = matrix.length;
visited.add(i);
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 1 && !visited.contains(j)) {
dfs(matrix, j, visited);
}
}
}
}
Go Code:
func findCircleNum(isConnected [][]int) int {
count := len(isConnected)
parent := make([]int, count)
for i := range parent {
parent[i] = i
}
find := func(x int) int {
for parent[x] != x {
parent[x] = parent[parent[x]]
x = parent[x]
}
return x
}
union := func(p, q int) {
rootP := find(p)
rootQ := find(q)
if rootP == rootQ {
return
}
parent[rootP] = rootQ
count--
}
for i := range isConnected {
for j := range isConnected[i] {
if isConnected[i][j] == 0 {
continue
}
union(i, j)
}
}
return count
}
复杂度分析
令 n 为数组长度。
powcai的代码:思路是深度优先遍历 class Solution: def findCircleNum(self, M: List[List[int]]) -> int: N = len(M) count = 0 visited = set()
def dfs(i):
for j in range(N):
if M[i][j] and j not in visited:
visited.add(j)
dfs(j)
for i in range(N):
if i not in visited:
count += 1
visited.add(i)
dfs(i)
return count
Union find.
class Solution:
def findCircleNum(self, M: List[List[int]]) -> int:
graph = collections.defaultdict(list)
if not M:
return 0
n = len(M)
for i in range(n):
for j in range(i+1,n):
if M[i][j]==1:
graph[i].append(j)
graph[j].append(i)
visit = [False]*n
def dfs(u):
for v in graph[u]:
if visit[v] == False:
visit[v] = True
dfs(v)
count = 0
for i in range(n):
if visit[i] == False:
count += 1
visit[i] = True
dfs(i)
return count
var findCircleNum = function (isConnected) {
let len = isConnected.length;
let parents = Array.from({ length: len }, (i, idx) => idx);
let ans = len
function find(x) {
if (parents[x] === x) return x;
parents[x] = find(parents[x]);
return parents[x];
}
function union(x, y) {
if (find(x) === find(y)) return;
parents[parents[x]] = parents[y];
ans--;
}
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (isConnected[i][j]) {
union(i, j);
}
}
}
return ans;
};
class quick_find(object):
def __init__(self,n):
self.root = list(range(n))
self.size = n
def find(self,x):
while self.root[x] != x:
x = self.root[x]
return x
def union(self,x,y):
rootx = self.find(x)
rooty = self.find(y)
if rootx != rooty:
self.root[rooty] = rootx
self.size -=1
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
uf = quick_find(len(isConnected))
for i in range(len(isConnected)):
for j in range(i+1,len(isConnected)):
if isConnected[i][j] == 1:
uf.union(i,j)
return uf.size
思路: dfs, check the adjacency matrix for the pairwise relationship
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length, result = 0;
boolean[] visited = new boolean[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(isConnected, visited, i);
result++;
}
}
return result;
}
private void dfs(int[][] isConnected, boolean[] visited, int i) {
visited[i] = true;
for (int j = 0; j < isConnected.length; j++) {
if (isConnected[i][j] == 1 && !visited[j]) {
dfs(isConnected, visited, j);
}
}
}
}
Time Complexity: O(n^2) Space Complexity: O(n)
并查集。
class Solution {
public:
int Find(vector<int>& parent, int index) {
if (parent[index] != index) parent[index] = Find(parent, parent[index]);
return parent[index];
}
void Union(vector<int>& parent, int index1, int index2) {
parent[Find(parent, index1)] = Find(parent, index2);
}
int findCircleNum(vector<vector<int>>& isConnected) {
int provinces = isConnected.size();
vector<int> parent(provinces);
for (int i = 0; i < provinces; i++) parent[i] = i;
for (int i = 0; i < provinces; i++) for (int j = i + 1; j < provinces; j++) if (isConnected[i][j] == 1) Union(parent, i, j);
int circles = 0;
for (int i = 0; i < provinces; i++) if (parent[i] == i) circles++;
return circles;
}
};
547. 省份数量
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/number-of-provinces/
前置知识
暂无
题目描述