leetcode
leetcode 301 ~ 350
无向图中连通分量的数目

无向图中连通分量的数目

难度:

标签:

题目描述

代码结果

运行时间: 26 ms, 内存: 17.4 MB


/*
 * 思路:
 * 给定一个无向图,要求计算图中的连通分量的数量。
 * 使用Java Stream API处理邻接表创建和节点遍历。
 * 使用DFS进行图的遍历。
 */
 
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class NumberOfConnectedComponentsStream {
    public int countComponents(int n, int[][] edges) {
        // 创建图的邻接表表示
        List<List<Integer>> graph = IntStream.range(0, n)
                .mapToObj(i -> new ArrayList<Integer>())
                .collect(Collectors.toList());
        Arrays.stream(edges).forEach(edge -> {
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);
        });
 
        // 记录访问过的节点
        boolean[] visited = new boolean[n];
        int count = 0;
 
        // 遍历每个节点
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                count++;
                dfs(graph, visited, i);
            }
        }
 
        return count;
    }
 
    // 深度优先搜索(DFS)方法
    private void dfs(List<List<Integer>> graph, boolean[] visited, int node) {
        visited[node] = true;
        graph.get(node).stream()
                .filter(neighbor -> !visited[neighbor])
                .forEach(neighbor -> dfs(graph, visited, neighbor));
    }
}
 

解释

方法:

这个题解使用深度优先搜索(DFS)来解决无向图中连通分量的数目问题。首先,通过遍历所有边构建图的邻接表表示。然后,对于每个节点,如果它还没有被访问过,就将连通分量的计数器加1,并从该节点开始进行DFS遍历,标记所有与之相连的节点为已访问。最终,连通分量的总数就是DFS遍历的次数。

时间复杂度:

O(V+E)

空间复杂度:

O(V+E)

代码细节讲解

🦆
如何确保通过构建的邻接表能够正确反映无向图中所有节点之间的关系,特别是对于孤立节点的处理?
在构建邻接表时,对于每对边(s, e),都需要将s添加到e的邻接列表中,同时将e添加到s的邻接列表中。这确保了无向图的双向关系得到反映。对于可能存在的孤立节点,即那些没有任何边与之相连的节点,它们在邻接表中可能不会显式出现。为确保这些节点也被考虑在内,可以在邻接表初始化时,为图中的每个节点预先创建一个空的邻接列表。这样,即使某些节点没有任何直接的边连接,它们也会在邻接表中有一个表示,从而在遍历所有节点时能正确统计到孤立节点。
🦆
在DFS遍历中,如果遇到具有大量节点和边的密集图,递归调用栈可能存在溢出的风险吗?存在的话,如何优化以避免这种情况?
在使用递归进行DFS遍历时,确实存在调用栈溢出的风险,特别是对于大规模的图和深图。为了避免这种情况,可以考虑使用迭代版本的DFS而不是递归。迭代版本的DFS使用显式的栈来模拟递归过程,从而避免了对系统调用栈的依赖。这样,即使是在非常大或深的图中,也不会出现栈溢出的问题。
🦆
在DFS函数中,递归之前没有特别检查`n`是否存在于图`g`中,这是否会影响算法的执行或导致错误?
在当前题解的DFS实现中,对于每个节点,首先检查它是否在邻接表`g`中。这是因为`g`可能不包含那些没有邻居的孤立节点。如果不进行这样的检查,对于孤立节点,程序将尝试访问`g[node]`时抛出错误,因为这样的键不存在于字典中。因此,这种检查是必要的,它确保了算法的健壮性,避免在尝试访问不存在的键时出错。
🦆
在处理连通分量的计数时,代码是如何确保每个新开始的DFS遍历都是从一个新的连通分量开始的?
在实现中,使用一个集合`visited`来记录已经访问过的节点。在遍历所有节点时,只有当发现一个节点还未被访问,即`node not in visited`时,才会开始一个新的DFS遍历,并将连通分量的计数器增加1。这确保了每次开始新的DFS遍历都对应于一个新的连通分量,因为只有未被访问的节点才会触发新的DFS调用,从而标记出一个新的连通分量的所有节点。

相关问题

岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

 

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 '0''1'

以图判树

以图判树

省份数量

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]10
  • isConnected[i][i] == 1
  • isConnected[i][j] == isConnected[j][i]