LeetCode 444 - 序列重建


文章目录

摘要

这道题是典型的"拓扑排序 + 唯一性判断"的组合题。我们不仅要判断能否从若干子序列中重建原序列,还要验证这个重建序列是不是唯一的。虽然听起来有点抽象,但一旦从"图论 + 顺序约束"的角度理解,整个思路就会清晰很多。

这题在实际工程里面其实很有应用价值,比如:

  • 根据操作日志恢复真实执行顺序
  • 事件流中根据局部约束恢复唯一事件链路
  • 构建依赖图时判断有没有唯一确定的执行顺序

文章中我会给出完整的 Swift 解法,并在 Demo 中直接运行。

描述

题目给你:

  • 一个整数数组 nums:这是你要恢复的最终序列;
  • 一个二维数组 sequences:每一条序列都只给出部分的顺序关系。

目标是判断:
这些局部序列能否唯一地拼出 nums

比如:

txt 复制代码
nums = [1,2,3]
sequences = [[1,2], [1,3]]

这时候顺序可能是 1 -> 2 -> 31 -> 3 -> 2

并不是唯一的,因此返回 false。

而:

txt 复制代码
nums = [1,2,3]
sequences = [[1,2], [2,3]]

局部约束拼起来得到的序列只能是 [1,2,3]

这是唯一一种顺序,因此返回 true。

题解答案

最关键的是两点:

  1. 拓扑排序构图:

    根据 sequences 中的相邻关系生成有向图,并记录入度。

  2. 唯一性判断:

    在拓扑排序过程中:
    如果某一时刻队列中有超过 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. 拓扑排序过程中必须始终保持队列只有 1 个节点

    这说明顺序唯一。

  2. 输出顺序必须严格等于 nums

    防止 sequences 给的是有效拓扑,但不是你想要的序列。

  3. sequences 必须覆盖所有元素

    否则一定无法重建。

如果把拓扑排序理解透彻,这题就是很标准的"唯一拓扑排序"问题,是图论中非常有代表性的场景。

相关推荐
云泽8083 小时前
蓝桥杯算法精讲:前缀和与差分算法的应用与实战
算法·职场和发展·蓝桥杯
NaturalHarmonia3 小时前
UIE信息抽取模型指代消解实战教程(extra)
人工智能·算法
Eloudy3 小时前
jacobi solver 迭代算法
人工智能·算法·机器学习
黑衣李3 小时前
csp-2019 选择题第十题
算法
草莓熊Lotso3 小时前
哈希表的两种灵魂:深入探索开放定址与链地址法的核心机密
linux·运维·数据结构·c++·人工智能·算法·哈希算法
wadesir3 小时前
高效存储与访问:Rust语言三角矩阵压缩(从零开始掌握Rust稀疏矩阵存储技巧)
算法·矩阵·rust
Aspect of twilight3 小时前
LeetCode华为2025年秋招AI大模型岗刷题(三)
python·算法·leetcode
有为少年3 小时前
神经网络 | 从线性结构到可学习非线性
人工智能·深度学习·神经网络·学习·算法·机器学习·信号处理
飞Link3 小时前
【论文笔记】《Improving action segmentation via explicit similarity measurement》
论文阅读·深度学习·算法·计算机视觉