

文章目录
摘要
这道题是典型的"拓扑排序 + 唯一性判断"的组合题。我们不仅要判断能否从若干子序列中重建原序列,还要验证这个重建序列是不是唯一的。虽然听起来有点抽象,但一旦从"图论 + 顺序约束"的角度理解,整个思路就会清晰很多。
这题在实际工程里面其实很有应用价值,比如:
- 根据操作日志恢复真实执行顺序
- 事件流中根据局部约束恢复唯一事件链路
- 构建依赖图时判断有没有唯一确定的执行顺序
文章中我会给出完整的 Swift 解法,并在 Demo 中直接运行。

描述
题目给你:
- 一个整数数组
nums:这是你要恢复的最终序列; - 一个二维数组
sequences:每一条序列都只给出部分的顺序关系。
目标是判断:
这些局部序列能否唯一地拼出 nums?
比如:
txt
nums = [1,2,3]
sequences = [[1,2], [1,3]]
这时候顺序可能是 1 -> 2 -> 3 或 1 -> 3 -> 2
并不是唯一的,因此返回 false。
而:
txt
nums = [1,2,3]
sequences = [[1,2], [2,3]]
局部约束拼起来得到的序列只能是 [1,2,3]
这是唯一一种顺序,因此返回 true。
题解答案
最关键的是两点:
-
拓扑排序构图:
根据 sequences 中的相邻关系生成有向图,并记录入度。
-
唯一性判断:
在拓扑排序过程中:
如果某一时刻队列中有超过 1 个可选节点,说明排序结果不唯一 → 直接 false。
最终如果唯一顺序和 nums 完全一致,返回 true,否则 false。

题解代码分析
下面这段代码可以直接在 Swift Playground 或 Xcode 中跑起来。
swift
import Foundation
class Solution {
func sequenceReconstruction(_ nums: [Int], _ sequences: [[Int]]) -> Bool {
let n = nums.count
if n == 0 { return false }
// 构建图
var graph = Array(repeating: [Int](), count: n + 1)
var indegree = Array(repeating: 0, count: n + 1)
var exists = Array(repeating: false, count: n + 1)
// 标记序列中出现过的元素
for seq in sequences {
for num in seq {
if num < 1 || num > n { return false }
exists[num] = true
}
}
// 如果 sequences 中没有包含全部元素,肯定无法重建
for i in 1...n {
if !exists[i] { return false }
}
// 建图与入度
for seq in sequences {
if seq.count < 2 { continue }
for i in 0..<seq.count - 1 {
let from = seq[i]
let to = seq[i + 1]
graph[from].append(to)
indegree[to] += 1
}
}
// BFS 拓扑排序(需要保证唯一性)
var queue = [Int]()
// 入度为 0 的节点加入队列
for i in 1...n {
if indegree[i] == 0 {
queue.append(i)
}
}
var index = 0
while !queue.isEmpty {
// 如果当前队列中超过 1 个节点 → 不唯一
if queue.count > 1 { return false }
let cur = queue.removeFirst()
// 顺序必须与 nums 一致
if nums[index] != cur { return false }
index += 1
// 邻接节点入度减一
for next in graph[cur] {
indegree[next] -= 1
if indegree[next] == 0 {
queue.append(next)
}
}
}
return index == n
}
}
// MARK: - Demo 测试代码
func runDemo() {
let sol = Solution()
let nums1 = [1,2,3]
let seqs1 = [[1,2],[1,3]]
print("示例 1:", sol.sequenceReconstruction(nums1, seqs1))
let nums2 = [1,2,3]
let seqs2 = [[1,2],[2,3]]
print("示例 2:", sol.sequenceReconstruction(nums2, seqs2))
let nums3 = [1]
let seqs3 = [[1]]
print("示例 3:", sol.sequenceReconstruction(nums3, seqs3))
}
runDemo()
示例测试及结果
下面按照 Demo 的输出说明结果:
示例 1
txt
nums = [1,2,3]
sequences = [[1,2], [1,3]]
输出:
txt
false
原因:
1 之后可以接 2 或 3,不唯一。
示例 2
txt
nums = [1,2,3]
sequences = [[1,2], [2,3]]
输出:
txt
true
所有局部关系唯一指向 [1,2,3]。
示例 3
txt
nums = [1]
sequences = [[1]]
输出:
txt
true
就一个数,当然唯一。
与实际场景结合
这个问题在真实开发中其实非常有意义。以下举几个常见场景:
1. 分布式系统中的事件顺序恢复
多个日志源分别记录部分事件顺序,比如日志 A 记录 1 -> 3,日志 B 记录 3 -> 4。
你需要判断最终的事件顺序是否唯一,不然就无法可靠恢复系统状态。
2. 构建任务依赖图
CI/CD 或构建系统里,任务之间有依赖关系。
某些工具会根据依赖关系判断构建顺序是否唯一,避免潜在竞态。
3. 系统执行轨迹分析
调试复杂业务时,系统可能会在不同模块留下部分执行顺序。
你能否从这些局部顺序还原唯一执行轨迹?
这题就是最小化版本。
4. 版本控制系统中的操作序列解析
有些操作具有前后依赖,系统需要判断提交顺序是否唯一。
这类场景都是"根据局部顺序重建全局顺序"的典型需求。
时间复杂度
- 建图遍历所有 sequences:
O(total length of sequences) - 拓扑排序:
O(n + edges)
整体为:
O(n + sum(sequences.length))
空间复杂度
- 图结构:O(n + edges)
- 入度数组、存在标记:O(n)
- 队列:O(n)
整体:
O(n + edges)
总结
这道题表面是拓扑排序,但核心在:
-
拓扑排序过程中必须始终保持队列只有 1 个节点
这说明顺序唯一。
-
输出顺序必须严格等于 nums
防止 sequences 给的是有效拓扑,但不是你想要的序列。
-
sequences 必须覆盖所有元素
否则一定无法重建。
如果把拓扑排序理解透彻,这题就是很标准的"唯一拓扑排序"问题,是图论中非常有代表性的场景。