← 返回编程题库
coding-graph-cycle-detect-undirected简单免费版2000ms未尝试

判断无向图是否存在环

Detect a Cycle in an Undirected Graph

开始编码

给一个无向图,包含 n 个节点(标号 0n - 1)以及一组边(每条边是无序对 [u, v])。如果图中存在任何环,返回 True,否则返回 False。自环 (u, u) 计为环;同一条边 (u, v) 出现两次也算(长度为 2 的)环。

请实现 solution(n: int, edges: list[list[int]]) -> bool。最干净的做法是并查集:把 parent[i] 初始化为 i,对每条边 (u, v) 询问 find(u)find(v)——若已相等,则 uv 同分量,这条边必构成环,返回 True;否则 union 后继续。整个边列表跑完都没出现「同根」的情况,说明图是森林(无环),返回 False

Examples

solution(4, [[0,1],[1,2],[2,3]]) 返回 False。图是路径 0—1—2—3:4 个节点的树。每条边都把两个不同分量合并,从未闭合任何环。

solution(3, [[0,1],[1,2],[2,0]]) 返回 True。处理完 (0,1)(1,2) 后三个点同分量;第三条 (2,0)find(2) == find(0),必然闭合环——三角形 0–1–2。

solution(1, [[0,0]]) 返回 True。节点 0 上的自环按定义就是无向图里长度 1 的环;显式判 u == vfind(0) == find(0) 都立即触发。

签名见 stubs/stub.py

Practical context

无向图判环是清算所与主经纪商交易图对账(trade-graph reconciliation)的核心内核。日终时,每笔已清算交易的每条腿都会成为两个对手方结算账户节点之间的一条边(买方付现给卖方、卖方交券给买方,加上三方抵押、FX 腿等)。对账引擎想知道两件事:(1) 哪些账户子集净额为零(连通分量——也是并查集),(2) 是否存在多余的对冲承诺——义务图里的环意味着某些腿可以被取消或压缩而不改变任何净头寸。主流的交易压缩服务(TriOptima/triReduce、LCH SwapClear 的 compression cycles、ICE Link)就是构造出这张图再跑环分解算法;并查集这一原语作为第一遍扫描,先判定到底「有没有环」,再决定是否启动昂贵的环枚举阶段。同样的原语也出现在风险图因子模型(检测分层因子与工具之间依赖 DAG 中的环——出现环说明因子配置错了,会导致协方差矩阵奇异)和抵押品再抵押链("A 抵押给 B、B 抵押给 C、C 抵押给 A" 这种环是监管红线)里。并查集在全公司级别的交易图(百万级边)上每次查询均摊 O(α(n)),这就是它在在线增量场景里相对 DFS 的标准地位。

约束条件

  • 0 ≤ n ≤ 10^4
  • 0 ≤ len(edges) ≤ 10^5
  • 每条边是 `[u, v]`,满足 `0 ≤ u, v < n`
  • 边是无向的,`(u, v)` 与 `(v, u)` 表示同一条边
  • 自环(u == v)和重复边都允许出现在输入里

样例

Case 1 · typical: tree (path graph) has no cycle

输入: [4,[[0,1],[1,2],[2,3]]]

期望: false

n=4,边构成一条链 0-1-2-3,是一棵树(n-1 条边连通无环)。每条边两端点 find 不同,依次 union,没有任何边把两个已连通的点再次连起来,返回 False。

Case 2 · typical: triangle is a cycle

输入: [3,[[0,1],[1,2],[2,0]]]

期望: true

三角形 0-1-2-0。前两条边把 {0,1,2} 并入同一组件;第三条边 (2,0) 时 find(2)==find(0),已连通——这条边形成环,返回 True。

Case 3 · simple: self-loop on a single node

输入: [1,[[0,0]]]

期望: true

u==v=0 是自环,无向图里它本身就是一个长度为 1 的环。算法在循环开头直接判定 u==v 返回 True(或等价地 find(u)==find(v) 因为同一节点)。

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

Case 1 · typical: tree (path graph) has no cycle

输入: [4,[[0,1],[1,2],[2,3]]]

期望: false

n=4,边构成一条链 0-1-2-3,是一棵树(n-1 条边连通无环)。每条边两端点 find 不同,依次 union,没有任何边把两个已连通的点再次连起来,返回 False。

Case 2 · typical: triangle is a cycle

输入: [3,[[0,1],[1,2],[2,0]]]

期望: true

三角形 0-1-2-0。前两条边把 {0,1,2} 并入同一组件;第三条边 (2,0) 时 find(2)==find(0),已连通——这条边形成环,返回 True。

Case 3 · simple: self-loop on a single node

输入: [1,[[0,0]]]

期望: true

u==v=0 是自环,无向图里它本身就是一个长度为 1 的环。算法在循环开头直接判定 u==v 返回 True(或等价地 find(u)==find(v) 因为同一节点)。