Welcome to Subscribe On Youtube

323. Number of Connected Components in an Undirected Graph

Description

You have a graph of n nodes. You are given an integer n and an array edges where edges[i] = [ai, bi] indicates that there is an edge between ai and bi in the graph.

Return the number of connected components in the graph.

 

Example 1:

Input: n = 5, edges = [[0,1],[1,2],[3,4]]
Output: 2

Example 2:

Input: n = 5, edges = [[0,1],[1,2],[2,3],[3,4]]
Output: 1

 

Constraints:

  • 1 <= n <= 2000
  • 1 <= edges.length <= 5000
  • edges[i].length == 2
  • 0 <= ai <= bi < n
  • ai != bi
  • There are no repeated edges.

Solutions

Solution 1

DFS

One solution is to use DFS. The idea is to give each node a flag to mark whether it has been visited. For an unvisited node, we will increment the result by 1, because this must be a new connected region. , And then we traverse the neighboring nodes through the adjacency list and mark them as visited.

After traversing all connected nodes, we continue to look for the next unvisited node, and so on until all nodes have been visited, then the number of connected regions is also calculated at this time

Union Find

Create a root array with the same subscript and node value. At this time, root[i] indicates that node i belongs to group i. We initialized n parts (res = n), assuming that each node belongs to a separate interval at the beginning .

Then we start to traverse all the edges. For two points of an edge, their values in the root at the beginning are not the same. At this time, we reduce the result by 1, which means one interval is missing. Then update the root of one of the nodes Value to make the root value of the two nodes the same.

Then we can mark the root values of all nodes in the connected interval as the same value, and the root values of different connected intervals are different, so that we can also find the number of connected intervals.

Solution 2: Union-Find

We can use a union-find set to maintain the connected components in the graph.

First, we initialize a union-find set, then traverse all the edges. For each edge $(a, b)$, we merge nodes $a$ and $b$ into the same connected component. If the merge is successful, it means that nodes $a$ and $b$ were not in the same connected component before, and the number of connected components decreases by one.

Finally, we return the number of connected components.

The time complexity is $O(n + m \times \alpha(n))$, and the space complexity is $O(n)$. Where $n$ and $m$ are the number of nodes and edges, respectively, and $\alpha(n)$ is the inverse of the Ackermann function, which can be regarded as a very small constant.

Solution 3: BFS

We can also use BFS (Breadth-First Search) to count the number of connected components in the graph.

Similar to Solution 1, we first construct an adjacency list $g$ based on the given edges. Then we traverse all nodes. For each node, if it has not been visited, we start BFS traversal from this node, marking all its adjacent nodes as visited, until all its adjacent nodes have been visited. In this way, we have found a connected component, and the answer is incremented by one.

After traversing all nodes, we get the number of connected components in the graph.

The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$. Where $n$ and $m$ are the number of nodes and edges, respectively.

Solution 4: Explicit Stack

Thinking

The number of connected components is the number of searches that start at an unvisited node and cover its component. With $n \le 2000$, one $O(n + m)$ traversal is enough. Recursing along a chain uses a call depth equal to the node count and overflows Python once the chain reaches length $1000$. Each new component is therefore expanded on a stack: a node is marked when it is pushed, and unvisited neighbors are pushed after it is popped. Marking on push keeps every node out of the stack more than once. Each new start increments the answer.

Build an adjacency list $g$ from the given edges. $g[i]$ holds every neighbor of node $i$.

Scan the nodes. When node $i$ is still unvisited, increment the answer, push $i$, and mark it visited. While the stack is not empty, pop node $u$ and push every neighbor that has not been visited, marking it at the same time. That search covers the component of $i$. Continue with the next unvisited node until every node has been seen. The order of the neighbors does not change the number of components.

The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$. Where $n$ and $m$ are the number of nodes and edges, respectively.

  • class Solution {
        private int[] p;
    
        public int countComponents(int n, int[][] edges) {
            p = new int[n];
            for (int i = 0; i < n; ++i) {
                p[i] = i;
            }
            for (int[] e : edges) {
                int a = e[0], b = e[1];
                p[find(a)] = find(b);
            }
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (i == find(i)) {
                    ++ans;
                }
            }
            return ans;
        }
    
        private int find(int x) {
            if (p[x] != x) {
                p[x] = find(p[x]);
            }
            return p[x];
        }
    }
    
    
    // Solution 2
    class UnionFind {
        private final int[] p;
        private final int[] size;
    
        public UnionFind(int n) {
            p = new int[n];
            size = new int[n];
            for (int i = 0; i < n; ++i) {
                p[i] = i;
                size[i] = 1;
            }
        }
    
        public int find(int x) {
            if (p[x] != x) {
                p[x] = find(p[x]);
            }
            return p[x];
        }
    
        public boolean union(int a, int b) {
            int pa = find(a), pb = find(b);
            if (pa == pb) {
                return false;
            }
            if (size[pa] > size[pb]) {
                p[pb] = pa;
                size[pa] += size[pb];
            } else {
                p[pa] = pb;
                size[pb] += size[pa];
            }
            return true;
        }
    }
    
    class Solution {
        public int countComponents(int n, int[][] edges) {
            UnionFind uf = new UnionFind(n);
            for (var e : edges) {
                n -= uf.union(e[0], e[1]) ? 1 : 0;
            }
            return n;
        }
    }
    
    
    
    // Solution 3
    class Solution {
        public int countComponents(int n, int[][] edges) {
            List<Integer>[] g = new List[n];
            Arrays.setAll(g, k -> new ArrayList<>());
            for (var e : edges) {
                int a = e[0], b = e[1];
                g[a].add(b);
                g[b].add(a);
            }
            int ans = 0;
            boolean[] vis = new boolean[n];
            for (int i = 0; i < n; ++i) {
                if (vis[i]) {
                    continue;
                }
                vis[i] = true;
                ++ans;
                Deque<Integer> q = new ArrayDeque<>();
                q.offer(i);
                while (!q.isEmpty()) {
                    int a = q.poll();
                    for (int b : g[a]) {
                        if (!vis[b]) {
                            vis[b] = true;
                            q.offer(b);
                        }
                    }
                }
            }
            return ans;
        }
    }
    
    
    
    // Solution 4
    class Solution {
        public int countComponents(int n, int[][] edges) {
            List<Integer>[] g = new List[n];
            Arrays.setAll(g, k -> new ArrayList<>());
            for (var e : edges) {
                int a = e[0], b = e[1];
                g[a].add(b);
                g[b].add(a);
            }
            boolean[] vis = new boolean[n];
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (vis[i]) {
                    continue;
                }
                ++ans;
                Deque<Integer> stk = new ArrayDeque<>();
                stk.push(i);
                vis[i] = true;
                while (!stk.isEmpty()) {
                    int u = stk.pop();
                    for (int v : g[u]) {
                        if (!vis[v]) {
                            vis[v] = true;
                            stk.push(v);
                        }
                    }
                }
            }
            return ans;
        }
    }
    
    
  • class Solution {
    public:
        int countComponents(int n, vector<vector<int>>& edges) {
            vector<int> p(n);
            iota(p.begin(), p.end(), 0);
            for (int i = 0; i < n; ++i) p[i] = i;
            function<int(int)> find = [&](int x) -> int {
                if (p[x] != x) p[x] = find(p[x]);
                return p[x];
            };
            for (auto& e : edges) {
                int a = e[0], b = e[1];
                p[find(a)] = find(b);
            }
            int ans = 0;
            for (int i = 0; i < n; ++i) ans += i == find(i);
            return ans;
        }
    };
    
    
    // Solution 2
    class UnionFind {
    public:
        UnionFind(int n) {
            p = vector<int>(n);
            size = vector<int>(n, 1);
            iota(p.begin(), p.end(), 0);
        }
    
        bool unite(int a, int b) {
            int pa = find(a), pb = find(b);
            if (pa == pb) {
                return false;
            }
            if (size[pa] > size[pb]) {
                p[pb] = pa;
                size[pa] += size[pb];
            } else {
                p[pa] = pb;
                size[pb] += size[pa];
            }
            return true;
        }
    
        int find(int x) {
            if (p[x] != x) {
                p[x] = find(p[x]);
            }
            return p[x];
        }
    
    private:
        vector<int> p, size;
    };
    
    class Solution {
    public:
        int countComponents(int n, vector<vector<int>>& edges) {
            UnionFind uf(n);
            for (auto& e : edges) {
                n -= uf.unite(e[0], e[1]);
            }
            return n;
        }
    };
    
    
    
    // Solution 3
    class Solution {
    public:
        int countComponents(int n, vector<vector<int>>& edges) {
            vector<int> g[n];
            for (auto& e : edges) {
                int a = e[0], b = e[1];
                g[a].push_back(b);
                g[b].push_back(a);
            }
            vector<bool> vis(n);
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (vis[i]) {
                    continue;
                }
                vis[i] = true;
                ++ans;
                queue<int> q{{i}};
                while (!q.empty()) {
                    int a = q.front();
                    q.pop();
                    for (int b : g[a]) {
                        if (!vis[b]) {
                            vis[b] = true;
                            q.push(b);
                        }
                    }
                }
            }
            return ans;
        }
    };
    
    
    
    // Solution 4
    class Solution {
    public:
        int countComponents(int n, vector<vector<int>>& edges) {
            vector<vector<int>> g(n);
            for (auto& e : edges) {
                int a = e[0], b = e[1];
                g[a].push_back(b);
                g[b].push_back(a);
            }
            vector<char> vis(n);
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (vis[i]) {
                    continue;
                }
                ++ans;
                vector<int> stk = {i};
                vis[i] = 1;
                while (!stk.empty()) {
                    int u = stk.back();
                    stk.pop_back();
                    for (int v : g[u]) {
                        if (!vis[v]) {
                            vis[v] = 1;
                            stk.push_back(v);
                        }
                    }
                }
            }
            return ans;
        }
    };
    
    
  • '''
    example-2: [[0, 1], [1, 2], [2, 3], [3, 4]]
    
    for loop
    
    [0, 1]
        0 parent set to 1
        1 parent set to 1
    
    [1, 2]
        1 parent set to 2
        2 parent set to 2
    
    [2, 3]
        2 parent set to 3
        3 parent set to 3
    
    final:
        0 parent set to 1
        1 parent set to 2
        2 parent set to 3
        3 parent set to 3
    
    ===> so filter for 'i == find(i)'
    '''
    class Solution:
        def countComponents(self, n: int, edges: List[List[int]]) -> int:
            def find(x):
                if p[x] != x:
                    p[x] = find(p[x])
                return p[x]
    
            p = list(range(n))
            for a, b in edges:
                p[find(a)] = find(b)
            return sum(i == find(i) for i in range(n)) # or len(set(parents))
    
    ############
    
    class Solution: # more literal
        def countComponents(self, n: int, edges: List[List[int]]) -> int:
            # Initialize parent array for union-find
            parent = [i for i in range(n)]
    
            # Perform union-find on each edge
            for u, v in edges:
                self.union(u, v, parent)
    
            # Count the number of distinct parents (connected components)
            count = len(set(self.find(x, parent) for x in range(n)))
            return count
    
        def union(self, x: int, y: int, parent: List[int]) -> None:
            rootX = self.find(x, parent)
            rootY = self.find(y, parent)
            if rootX != rootY:
                parent[rootX] = rootY
    
        def find(self, x: int, parent: List[int]) -> int:
            if parent[x] != x:
                parent[x] = self.find(parent[x], parent)
            return parent[x]
    
    
    # Solution 2
    class UnionFind:
        def __init__(self, n):
            self.p = list(range(n))
            self.size = [1] * n
    
        def find(self, x):
            if self.p[x] != x:
                self.p[x] = self.find(self.p[x])
            return self.p[x]
    
        def union(self, a, b):
            pa, pb = self.find(a), self.find(b)
            if pa == pb:
                return False
            if self.size[pa] > self.size[pb]:
                self.p[pb] = pa
                self.size[pa] += self.size[pb]
            else:
                self.p[pa] = pb
                self.size[pb] += self.size[pa]
            return True
    
    
    class Solution:
        def countComponents(self, n: int, edges: List[List[int]]) -> int:
            uf = UnionFind(n)
            for a, b in edges:
                n -= uf.union(a, b)
            return n
    
    
    
    # Solution 3
    class Solution:
        def countComponents(self, n: int, edges: List[List[int]]) -> int:
            g = [[] for _ in range(n)]
            for a, b in edges:
                g[a].append(b)
                g[b].append(a)
            vis = set()
            ans = 0
            for i in range(n):
                if i in vis:
                    continue
                vis.add(i)
                q = deque([i])
                while q:
                    a = q.popleft()
                    for b in g[a]:
                        if b not in vis:
                            vis.add(b)
                            q.append(b)
                ans += 1
            return ans
    
    
    
    # Solution 4
    class Solution:
        def countComponents(self, n: int, edges: List[List[int]]) -> int:
            g = [[] for _ in range(n)]
            for a, b in edges:
                g[a].append(b)
                g[b].append(a)
            vis = [False] * n
            ans = 0
            for i in range(n):
                if vis[i]:
                    continue
                ans += 1
                stk = [i]
                vis[i] = True
                while stk:
                    u = stk.pop()
                    for v in g[u]:
                        if not vis[v]:
                            vis[v] = True
                            stk.append(v)
            return ans
    
    
  • func countComponents(n int, edges [][]int) (ans int) {
    	p := make([]int, n)
    	for i := range p {
    		p[i] = i
    	}
    	var find func(int) int
    	find = func(x int) int {
    		if p[x] != x {
    			p[x] = find(p[x])
    		}
    		return p[x]
    	}
    	for _, e := range edges {
    		a, b := e[0], e[1]
    		p[find(a)] = find(b)
    	}
    	for i := 0; i < n; i++ {
    		if i == find(i) {
    			ans++
    		}
    	}
    	return
    }
    
    
    // Solution 2
    type unionFind struct {
    	p, size []int
    }
    
    func newUnionFind(n int) *unionFind {
    	p := make([]int, n)
    	size := make([]int, n)
    	for i := range p {
    		p[i] = i
    		size[i] = 1
    	}
    	return &unionFind{p, size}
    }
    
    func (uf *unionFind) find(x int) int {
    	if uf.p[x] != x {
    		uf.p[x] = uf.find(uf.p[x])
    	}
    	return uf.p[x]
    }
    
    func (uf *unionFind) union(a, b int) bool {
    	pa, pb := uf.find(a), uf.find(b)
    	if pa == pb {
    		return false
    	}
    	if uf.size[pa] > uf.size[pb] {
    		uf.p[pb] = pa
    		uf.size[pa] += uf.size[pb]
    	} else {
    		uf.p[pa] = pb
    		uf.size[pb] += uf.size[pa]
    	}
    	return true
    }
    
    func countComponents(n int, edges [][]int) int {
    	uf := newUnionFind(n)
    	for _, e := range edges {
    		if uf.union(e[0], e[1]) {
    			n--
    		}
    	}
    	return n
    }
    
    
    
    // Solution 3
    func countComponents(n int, edges [][]int) (ans int) {
    	g := make([][]int, n)
    	for _, e := range edges {
    		a, b := e[0], e[1]
    		g[a] = append(g[a], b)
    		g[b] = append(g[b], a)
    	}
    	vis := make([]bool, n)
    	for i := range g {
    		if vis[i] {
    			continue
    		}
    		vis[i] = true
    		ans++
    		q := []int{i}
    		for len(q) > 0 {
    			a := q[0]
    			q = q[1:]
    			for _, b := range g[a] {
    				if !vis[b] {
    					vis[b] = true
    					q = append(q, b)
    				}
    			}
    		}
    	}
    	return
    }
    
    
    
    // Solution 4
    func countComponents(n int, edges [][]int) int {
    	g := make([][]int, n)
    	for _, e := range edges {
    		a, b := e[0], e[1]
    		g[a] = append(g[a], b)
    		g[b] = append(g[b], a)
    	}
    	vis := make([]bool, n)
    	ans := 0
    	for i := 0; i < n; i++ {
    		if vis[i] {
    			continue
    		}
    		ans++
    		stk := []int{i}
    		vis[i] = true
    		for len(stk) > 0 {
    			u := stk[len(stk)-1]
    			stk = stk[:len(stk)-1]
    			for _, v := range g[u] {
    				if !vis[v] {
    					vis[v] = true
    					stk = append(stk, v)
    				}
    			}
    		}
    	}
    	return ans
    }
    
    
  • /**
     * @param {number} n
     * @param {number[][]} edges
     * @return {number}
     */
    var countComponents = function (n, edges) {
        let p = new Array(n);
        for (let i = 0; i < n; ++i) {
            p[i] = i;
        }
        function find(x) {
            if (p[x] != x) {
                p[x] = find(p[x]);
            }
            return p[x];
        }
        for (const [a, b] of edges) {
            p[find(a)] = find(b);
        }
        let ans = 0;
        for (let i = 0; i < n; ++i) {
            if (i == find(i)) {
                ++ans;
            }
        }
        return ans;
    };
    
    
    
    // Solution 4
    /**
     * @param {number} n
     * @param {number[][]} edges
     * @return {number}
     */
    var countComponents = function (n, edges) {
        const g = Array.from({ length: n }, () => []);
        for (const [a, b] of edges) {
            g[a].push(b);
            g[b].push(a);
        }
        const vis = Array(n).fill(false);
        let ans = 0;
        for (let i = 0; i < n; ++i) {
            if (vis[i]) {
                continue;
            }
            ++ans;
            const stk = [i];
            vis[i] = true;
            while (stk.length) {
                const u = stk.pop();
                for (const v of g[u]) {
                    if (!vis[v]) {
                        vis[v] = true;
                        stk.push(v);
                    }
                }
            }
        }
        return ans;
    };
    
    
  • function countComponents(n: number, edges: number[][]): number {
        const g: number[][] = Array.from({ length: n }, () => []);
        for (const [a, b] of edges) {
            g[a].push(b);
            g[b].push(a);
        }
        const vis: boolean[] = Array(n).fill(false);
        const dfs = (i: number): number => {
            if (vis[i]) {
                return 0;
            }
            vis[i] = true;
            for (const j of g[i]) {
                dfs(j);
            }
            return 1;
        };
        return g.reduce((acc, _, i) => acc + dfs(i), 0);
    }
    
    
    // Solution 2
    class UnionFind {
        p: number[];
        size: number[];
        constructor(n: number) {
            this.p = Array(n)
                .fill(0)
                .map((_, i) => i);
            this.size = Array(n).fill(1);
        }
    
        find(x: number): number {
            if (this.p[x] !== x) {
                this.p[x] = this.find(this.p[x]);
            }
            return this.p[x];
        }
    
        union(a: number, b: number): boolean {
            const [pa, pb] = [this.find(a), this.find(b)];
            if (pa === pb) {
                return false;
            }
            if (this.size[pa] > this.size[pb]) {
                this.p[pb] = pa;
                this.size[pa] += this.size[pb];
            } else {
                this.p[pa] = pb;
                this.size[pb] += this.size[pa];
            }
            return true;
        }
    }
    
    function countComponents(n: number, edges: number[][]): number {
        const uf = new UnionFind(n);
        for (const [a, b] of edges) {
            n -= uf.union(a, b) ? 1 : 0;
        }
        return n;
    }
    
    
    
    // Solution 3
    function countComponents(n: number, edges: number[][]): number {
        const g: Map<number, number[]> = new Map(Array.from({ length: n }, (_, i) => [i, []]));
        for (const [a, b] of edges) {
            g.get(a)!.push(b);
            g.get(b)!.push(a);
        }
    
        const vis = new Set<number>();
        let ans = 0;
        for (const [i] of g) {
            if (vis.has(i)) {
                continue;
            }
            const q = [i];
            for (const j of q) {
                if (vis.has(j)) {
                    continue;
                }
                vis.add(j);
                q.push(...g.get(j)!);
            }
            ans++;
        }
        return ans;
    }
    
    
    
    // Solution 4
    function countComponents(n: number, edges: number[][]): number {
        const g: number[][] = Array.from({ length: n }, () => []);
        for (const [a, b] of edges) {
            g[a].push(b);
            g[b].push(a);
        }
        const vis: boolean[] = Array(n).fill(false);
        let ans = 0;
        for (let i = 0; i < n; ++i) {
            if (vis[i]) {
                continue;
            }
            ++ans;
            const stk: number[] = [i];
            vis[i] = true;
            while (stk.length) {
                const u = stk.pop()!;
                for (const v of g[u]) {
                    if (!vis[v]) {
                        vis[v] = true;
                        stk.push(v);
                    }
                }
            }
        }
        return ans;
    }
    
    

All Problems

All Solutions