跳转至

2685. 统计完全连通分量的数量

题目描述

给你一个整数 n 。现有一个包含 n 个顶点的 无向 图,顶点按从 0n - 1 编号。给你一个二维整数数组 edges 其中 edges[i] = [ai, bi] 表示顶点 aibi 之间存在一条 无向 边。

返回图中 完全连通分量 的数量。

如果在子图中任意两个顶点之间都存在路径,并且子图中没有任何一个顶点与子图外部的顶点共享边,则称其为 连通分量

如果连通分量中每对节点之间都存在一条边,则称其为 完全连通分量

 

示例 1:

输入:n = 6, edges = [[0,1],[0,2],[1,2],[3,4]]
输出:3
解释:如上图所示,可以看到此图所有分量都是完全连通分量。

示例 2:

输入:n = 6, edges = [[0,1],[0,2],[1,2],[3,4],[3,5]]
输出:1
解释:包含节点 0、1 和 2 的分量是完全连通分量,因为每对节点之间都存在一条边。
包含节点 3 、4 和 5 的分量不是完全连通分量,因为节点 4 和 5 之间不存在边。
因此,在图中完全连接分量的数量是 1 。

 

提示:

  • 1 <= n <= 50
  • 0 <= edges.length <= n * (n - 1) / 2
  • edges[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi
  • 不存在重复的边

解法

方法一:DFS

我们先根据题目给定的边建立一个邻接表 $g$,其中 $g[i]$ 表示顶点 $i$ 的邻接点集合。

然后我们从 $0$ 开始遍历所有顶点,如果当前顶点没有被访问过,我们就从当前顶点开始进行深度优先搜索,统计当前连通分量的顶点数 $x$ 和边数 $y$。如果 $\frac{x(x-1)}{2} = y$,那么当前连通分量就是完全连通分量,我们将答案加一。

最后我们返回答案即可。

时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别是顶点数和边数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
    def countCompleteComponents(self, n: int, edges: List[List[int]]) -> int:
        def dfs(i: int) -> (int, int):
            vis[i] = True
            x, y = 1, len(g[i])
            for j in g[i]:
                if not vis[j]:
                    a, b = dfs(j)
                    x += a
                    y += b
            return x, y

        g = defaultdict(list)
        for a, b in edges:
            g[a].append(b)
            g[b].append(a)
        vis = [False] * n
        ans = 0
        for i in range(n):
            if not vis[i]:
                a, b = dfs(i)
                ans += a * (a - 1) == b
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
    private List<Integer>[] g;
    private boolean[] vis;

    public int countCompleteComponents(int n, int[][] edges) {
        g = new List[n];
        vis = new boolean[n];
        Arrays.setAll(g, k -> new ArrayList<>());
        for (int[] e : edges) {
            int a = e[0], b = e[1];
            g[a].add(b);
            g[b].add(a);
        }
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            if (!vis[i]) {
                int[] t = dfs(i);
                if (t[0] * (t[0] - 1) == t[1]) {
                    ++ans;
                }
            }
        }
        return ans;
    }

    private int[] dfs(int i) {
        vis[i] = true;
        int x = 1, y = g[i].size();
        for (int j : g[i]) {
            if (!vis[j]) {
                int[] t = dfs(j);
                x += t[0];
                y += t[1];
            }
        }
        return new int[] {x, y};
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
public:
    int countCompleteComponents(int n, vector<vector<int>>& edges) {
        vector<vector<int>> g(n);
        bool vis[n];
        memset(vis, false, sizeof(vis));
        for (auto& e : edges) {
            int a = e[0], b = e[1];
            g[a].push_back(b);
            g[b].push_back(a);
        }
        function<pair<int, int>(int)> dfs = [&](int i) -> pair<int, int> {
            vis[i] = true;
            int x = 1, y = g[i].size();
            for (int j : g[i]) {
                if (!vis[j]) {
                    auto [a, b] = dfs(j);
                    x += a;
                    y += b;
                }
            }
            return make_pair(x, y);
        };
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            if (!vis[i]) {
                auto [a, b] = dfs(i);
                if (a * (a - 1) == b) {
                    ++ans;
                }
            }
        }
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func countCompleteComponents(n int, edges [][]int) (ans int) {
    g := make([][]int, n)
    vis := make([]bool, n)
    for _, e := range edges {
        a, b := e[0], e[1]
        g[a] = append(g[a], b)
        g[b] = append(g[b], a)
    }
    var dfs func(int) (int, int)
    dfs = func(i int) (int, int) {
        vis[i] = true
        x, y := 1, len(g[i])
        for _, j := range g[i] {
            if !vis[j] {
                a, b := dfs(j)
                x += a
                y += b
            }
        }
        return x, y
    }
    for i := range vis {
        if !vis[i] {
            a, b := dfs(i)
            if a*(a-1) == b {
                ans++
            }
        }
    }
    return
}

方法二:取巧做法

要解决的问题:

  1. 如何保存每一个节点与其它点联通状态
  2. 如何判断多个点是否是一个联通图

对于第一点:实际上就是保存了当前到每个点的联通点集合(包括自己),方便后续判等。 第二点:有了第一点之后,如果是连通图中的点就有:

  1. 此点包含此联通图中所有的点(包括自己)
  2. 并且只包含此联通图中的点

拿示例一举例:

  • 5 包含的联通点有且只有自己,所以是连通图
  • 0 包含 0、1、2,同理 1、2 点也是
  • 3 和 4 也是包含自己和彼此
  • 基于以上就有以下代码实现:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
    int countCompleteComponents(int n, vector<vector<int>>& edges) {
        int ans = 0;
        vector<set<int>> m(n + 1, set<int>());
        for (int i = 0; i < n; i++) {
            m[i].insert(i);
        }
        for (auto x : edges) {
            m[x[0]].insert(x[1]);
            m[x[1]].insert(x[0]);
        }
        map<set<int>, int> s;
        for (int i = 0; i < n; i++) {
            s[m[i]]++;
        }
        for (auto& [x, y] : s) {
            if (y == x.size()) {
                ans++;
            }
        }
        return ans;
    }
};

评论