Swift 图论实战:DFS 算法解锁 LeetCode 323 连通分量个数


文章目录

摘要

图是算法中最具挑战性的结构之一,而"连通分量"这个词听起来也有点像社交网络里的"圈子"概念。给你一张无向图,节点编号从 0 到 n-1,现在请你找出这个图中到底有多少个互相连着的群体(连通分量)

这题其实在很多实际问题里都能找到身影,比如社交图谱分析、网络故障检测、孤岛计算等等。

这篇文章将用 Swift 带你搞懂这题背后的图遍历方法(DFS 和并查集两种思路),并提供完整代码与解释。

描述

给定一个由 n 个节点(编号为 0n - 1)组成的无向图和一个边列表 edges,请你计算图中连通分量的数量。

示例

输入:

swift 复制代码
n = 5
edges = [[0, 1], [1, 2], [3, 4]]

输出:

txt 复制代码
2

解释:图中有两个连通分量:{0,1,2} 和 {3,4}

题解答案

这题有两个常见解法:

DFS 遍历每个连通区域

把图看成是一个邻接表,然后从没访问过的点开始 DFS,把一个区域内的所有点标记为访问过。每发现一个新未访问的节点,就说明有一个新的连通分量。

Union-Find(并查集)

通过并查集把每个点合并成"祖宗节点",合并所有连通的点,最后统计有多少个不同的祖宗节点,就是连通分量的数量。

我们下面会实现 DFS 方法,它更直观易懂,特别适合初学者。

题解代码分析(Swift 实现:DFS)

swift 复制代码
func countComponents(_ n: Int, _ edges: [[Int]]) -> Int {
    var graph = [Int: [Int]]()
    for edge in edges {
        graph[edge[0], default: []].append(edge[1])
        graph[edge[1], default: []].append(edge[0])
    }

    var visited = Set<Int>()
    var components = 0

    func dfs(_ node: Int) {
        if visited.contains(node) { return }
        visited.insert(node)
        for neighbor in graph[node, default: []] {
            dfs(neighbor)
        }
    }

    for i in 0..<n {
        if !visited.contains(i) {
            components += 1
            dfs(i)
        }
    }

    return components
}

题解代码详解

构建邻接表

swift 复制代码
var graph = [Int: [Int]]()
for edge in edges {
    graph[edge[0], default: []].append(edge[1])
    graph[edge[1], default: []].append(edge[0])
}

这段代码会把每一对连接关系存进字典,形成一个"谁连着谁"的列表。

DFS 深度优先搜索

swift 复制代码
func dfs(_ node: Int) {
    if visited.contains(node) { return }
    visited.insert(node)
    for neighbor in graph[node, default: []] {
        dfs(neighbor)
    }
}

从某个起点开始,一路访问下去,把整个连通区域的点都标记为"访问过"。

遍历所有节点

swift 复制代码
for i in 0..<n {
    if !visited.contains(i) {
        components += 1
        dfs(i)
    }
}

每当我们发现一个还没被访问的点,就说明它是一个新连通分量的起点,我们就从它出发去搜索这个"朋友圈"。

示例测试及结果

示例 1

swift 复制代码
let count1 = countComponents(5, [[0, 1], [1, 2], [3, 4]])
print(count1) // 输出:2

示例 2

swift 复制代码
let count2 = countComponents(4, [[0, 1], [2, 3]])
print(count2) // 输出:2

示例 3

swift 复制代码
let count3 = countComponents(5, [[0, 1], [1, 2], [2, 3], [3, 4]])
print(count3) // 输出:1

时间复杂度分析

  • 构建图:O(E)
  • DFS 总遍历所有节点和边:O(N + E)
  • 总体时间复杂度:O(N + E),其中 N 是节点数,E 是边数

空间复杂度分析

  • 图邻接表:O(N + E)
  • 访问集合:O(N)
  • DFS 栈空间:O(N)
  • 总体空间复杂度:O(N + E)

总结

这道题非常适合作为图算法入门练手题,掌握它你会收获:

  • 如何从边列表构建图结构
  • 如何用 DFS 找出连通区域
  • 连通分量的概念实际是"有几个不相交的图"
相关推荐
zh_xuan1 天前
LeeCode92. 反转链表II
数据结构·算法·链表·leecode
2401_841495641 天前
【数据结构】汉诺塔问题
java·数据结构·c++·python·算法·递归·
Q741_1471 天前
C++ 位运算 高频面试考点 力扣137. 只出现一次的数字 II 题解 每日一题
c++·算法·leetcode·面试·位运算
天特肿瘤电场研究所1 天前
专业的肿瘤电场疗法厂家
算法
DASXSDW1 天前
NET性能优化-使用RecyclableBuffer取代RecyclableMemoryStream
java·算法·性能优化
kfepiza1 天前
CAS (Compare and Swap) 笔记251007
java·算法
墨染点香1 天前
LeetCode 刷题【103. 二叉树的锯齿形层序遍历、104. 二叉树的最大深度、105. 从前序与中序遍历序列构造二叉树】
算法·leetcode·职场和发展
啊我不会诶1 天前
23ICPC澳门站补题
算法·深度优先·图论
Brookty1 天前
【算法】二分查找(一)朴素二分
java·学习·算法·leetcode·二分查找
黑色的山岗在沉睡1 天前
LeetCode 2761. 和等于目标值的质数对
算法·leetcode·职场和发展