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 找出连通区域
  • 连通分量的概念实际是"有几个不相交的图"
相关推荐
杂雾无尘34 分钟前
Swift 5.9 新特性揭秘:非复制类型的安全与高效
ios·swift·apple
jz_ddk1 小时前
[实战]调频(FM)和调幅(AM)信号生成(完整C语言实现)
c语言·算法·信号处理
CloudAce云一2 小时前
谷歌云代理商:谷歌云TPU/GPU如何加速您的AI模型训练和推理
算法
轻语呢喃2 小时前
每日LeetCode : 杨辉三角
javascript·后端·算法
YuTaoShao3 小时前
【LeetCode 热题 100】148. 排序链表——(解法二)分治
java·算法·leetcode·链表
Shilong Wang3 小时前
三维旋转沿轴分解
算法·计算机视觉·机器人
ygming3 小时前
Q43- code973- 最接近原点的 K 个点 + Q44- code347- 前 K 个高频元素
前端·算法
lightqjx3 小时前
【数据结构】顺序表(sequential list)
c语言·开发语言·数据结构·算法
ygming3 小时前
Hashmap/ Hashset- Q39~Q42内容
前端·算法
蒟蒻小袁4 小时前
力扣面试150题--全排列
算法·leetcode·面试