leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 76 】2021-11-24 - 547. 省份数量 #95

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

547. 省份数量

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/number-of-provinces/

前置知识

暂无

题目描述

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:


输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:

输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3
 

提示:

1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j] 为 1 或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]
yanglr commented 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];
    }
};

复杂度分析:

chakochako commented 2 years ago
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
zhangzz2015 commented 2 years ago

思路

关键点

代码

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();  
    }
};
yachtcoder commented 2 years ago

冰茶几

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
for123s commented 2 years ago

代码

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;
    }
};
kidexp commented 2 years ago

thoughts

通过union find里面找连通图的个数

code

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

complexity

Time O(n^2)

Space O(n)

thinkfurther commented 2 years ago
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)
biancaone commented 2 years ago
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
zhy3213 commented 2 years ago

思路

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
zjsuper commented 2 years ago

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
heyqz commented 2 years ago
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)]))
pophy commented 2 years ago

思路

Java Code

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--;
            }
        }
    }
}

时间&空间

littlemoon-zh commented 2 years ago

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
banjingking commented 2 years ago

思路

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;
}
}
laofuWF commented 2 years ago
# 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
skinnyh commented 2 years ago

Note

Solution

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)

JiangyanLiNEU commented 2 years ago

JavaScript

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;
};

Python

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
yan0327 commented 2 years ago

思路 并查集

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)

shawncvv commented 2 years ago

思路

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);
      }
    }
  }
};

复杂度

okbug commented 2 years ago
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;
    }
};
ZacheryCao commented 2 years ago

Idea:

Union-find

Code

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

Complexity:

Time : O(N^2) Space : O(N)

wangzehan123 commented 2 years ago

代码

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]++;
            }
        }
    }
}
ysy0707 commented 2 years ago

思路: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)

pan-qin commented 2 years ago

Idea:

basically the question is to find out the number of connected components in the graph. Use union-find

Complexity:

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;
        }

    }
}
tongxw commented 2 years ago

思路

并查集模板

代码


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)
ForLittleBeauty commented 2 years ago

思路


面试里暂时不会考并查集,所以练练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)

15691894985 commented 2 years ago

day【74】题目地址(547. 省份数量)

https://leetcode-cn.com/problems/number-of-provinces/

并查集求联通分量的个数,合并过程中记录 BFS和DFS均可

空间复杂度:O(n)记录每个城市所属联通的祖先

CoreJa commented 2 years ago

思路

并查集和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
Francis-xsc commented 2 years ago

思路

并查集

代码


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;
    }
};

复杂度分析

ghost commented 2 years ago

题目

  1. Number of Provinces

思路

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)

joeytor commented 2 years ago

思路

使用并查集的思路

遍历每个城市 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) 并查集的空间复杂度

xj-yan commented 2 years ago
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)

RocJeMaintiendrai commented 2 years ago

代码

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)

ychen8777 commented 2 years ago

思路

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)

septasset commented 2 years ago

Day 76

思考

并查集解,图论解似乎也可以

关键点

代码(Python)

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)

复杂度分析

zhuzuojun commented 2 years ago
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;
    }
};
naomiwufzz commented 2 years ago

思路1:并查集

套模板,相连的城市就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

复杂度分析

思路2:dfs

用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

复杂度分析

思路3:bfs

用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

复杂度分析

ai2095 commented 2 years ago

547. Number of Provinces

https://leetcode.com/problems/number-of-provinces/submissions/

Topics

-union find

思路

union find with size。

代码 Python

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)

Mrhero-web commented 2 years ago

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;
    }

}

}

chenming-cao commented 2 years ago

解题思路

并查集。此题为集合的合并与查询问题,适合用并查集。省份数量就是连通域数量。

代码

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];
    }
}

复杂度分析

Moin-Jer commented 2 years ago

思路


并查集

代码


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];
    }
}

复杂度分析


zhangyalei1026 commented 2 years ago
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;
    }
}
mixtureve commented 2 years ago
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);
            }
        }   
    }
}
learning-go123 commented 2 years ago

思路

代码

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 为数组长度。

jocelinLX commented 2 years ago

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 
LareinaWei commented 2 years ago

Thinking

Union find.

Code

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
chun1hao commented 2 years ago
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;
};
watermelonDrip commented 2 years ago
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
jiahui-z commented 2 years ago

思路: 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)

Daniel-Zheng commented 2 years ago

思路

并查集。

代码(C++)

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;
    }
};

复杂度分析