当前位置: 首页 > backend >正文

Swift 实战:用最长递增子序列算法解“俄罗斯套娃信封”问题(LeetCode 354)

在这里插入图片描述
在这里插入图片描述

文章目录

    • 摘要
    • 描述
    • 解决方案
    • 解析问题和答案
      • 细节解释
    • 示例和结果
    • 时间复杂度
    • 空间复杂度
    • 总结

摘要

这道题表面是“套娃游戏”,实际上是一个二维排序 + 最长递增子序列(LIS)的经典组合题。我们要在一堆信封中,找出能一层套一层的最大数量。本文会从零开始分析题目、拆解解法、实现一个 Swift 可运行 Demo,并结合实际场景聊聊为什么这种思路很常见。看完之后,你不仅能秒杀这题,还能举一反三,处理各种二维排序 + LIS 的场景。

描述

题目给我们一组信封,每个信封用 [w, h] 表示宽度和高度。要求在不旋转信封的前提下,找出能层层套入的最大信封数量。

规则:

  • 信封 A 可以套进信封 B,当且仅当 wA < wB && hA < hB
  • 不能旋转信封(也就是不能交换宽高)。
  • 要求返回最多能套多少层。

示例 1:

输入: [[5,4],[6,4],[6,7],[2,3]]
输出: 3
解释: [2,3] -> [5,4] -> [6,7]

示例 2:

输入: [[1,1],[1,1],[1,1]]
输出: 1

数据范围:

  • 1 <= envelopes.count <= 10^5
  • 1 <= wi, hi <= 10^5

解决方案

这题的高效解法是:

  1. 排序处理宽度

    • width 升序排序。
    • 如果宽度相同,按 height 降序排序(防止宽相等但高递增的情况被误当成可套娃)。
  2. 在高度序列上找 LIS

    • 排序后,问题变成了在“高度”序列中找最长严格递增子序列。
    • 高度序列的 LIS 长度,就是最大套娃数量。
  3. 二分优化 LIS

    • 常规 LIS 是 O(n²),但这里 n 可达 10^5,需要用二分法把复杂度降到 O(n log n)。

这个套路其实在很多二维比较的题目里都适用,比如“安排会议”、“选拔球员”等等。

解析问题和答案

下面是完整可运行的 Swift 代码。为了方便跑样例,我在同一个文件里加了测试函数。

import Foundationstruct RussianDollEnvelopes {func maxEnvelopes(_ envelopes: [[Int]]) -> Int {guard !envelopes.isEmpty else { return 0 }// 1. 按宽升序,高降序排序let sortedEnvelopes = envelopes.sorted { a, b inif a[0] == b[0] {return a[1] > b[1] // 宽相等时,高度降序} else {return a[0] < b[0] // 否则宽升序}}// 2. 提取高度序列let heights = sortedEnvelopes.map { $0[1] }// 3. 在 heights 上找 LIS(严格递增)return lengthOfLIS(heights)}// 二分优化的 LISprivate func lengthOfLIS(_ nums: [Int]) -> Int {var dp = [Int]()for num in nums {// 找到第一个 >= num 的位置var left = 0var right = dp.countwhile left < right {let mid = (left + right) / 2if dp[mid] < num {left = mid + 1} else {right = mid}}if left < dp.count {dp[left] = num} else {dp.append(num)}}return dp.count}
}

细节解释

  1. 为什么宽相等时要按高降序?
    假设有 [3,3][3,4],如果按高升序,LIS 会错误地认为 [3,3] -> [3,4] 可套娃,但宽相等所以不行。降序能破坏这种错误递增。

  2. 二分法找位置
    dp 数组是“当前长度的递增子序列的最小尾值”,二分查找能 O(log n) 定位替换位置。

  3. 时间复杂度

    • 排序 O(n log n)
    • LIS O(n log n)
    • 总体 O(n log n),n = envelopes.count
  4. 空间复杂度

    • 排序需要 O(log n) 栈空间
    • dp 最多长度为 n,O(n) 空间

示例和结果

func testRussianDollEnvelopes() {let solver = RussianDollEnvelopes()let case1 = [[5,4],[6,4],[6,7],[2,3]]print("Case 1 Result:", solver.maxEnvelopes(case1)) // 3let case2 = [[1,1],[1,1],[1,1]]print("Case 2 Result:", solver.maxEnvelopes(case2)) // 1let case3 = [[4,5],[4,6],[6,7],[2,3],[1,1]]print("Case 3 Result:", solver.maxEnvelopes(case3)) // 4let case4 = [[2,100],[3,200],[4,300],[5,500],[5,400],[5,250],[6,370],[6,360],[7,380]]print("Case 4 Result:", solver.maxEnvelopes(case4)) // 5
}testRussianDollEnvelopes()

输出示例:

Case 1 Result: 3
Case 2 Result: 1
Case 3 Result: 4
Case 4 Result: 5

这几组用例覆盖了常规、重复、边界和复杂排序的情况。

时间复杂度

  • 排序:O(n log n)
  • LIS(二分法):O(n log n)
  • 总体:O(n log n)
    可以轻松处理 n=10^5 级别的数据。

空间复杂度

  • 额外空间主要是存放 dp 数组和排序临时空间:O(n)。
  • 没有额外递归开销,空间使用很稳定。

总结

这题其实是“二维 LIS”的经典模板:

  1. 先排序破坏非法情况:宽升序 + 宽相等高降序。
  2. 在一维上做 LIS:把问题降维到 O(n log n) 的 LIS。
  3. 实现细节很关键:高降序是防错的核心,二分法是提速的关键。

现实中,这种“两个维度都要递增”的需求很常见,比如信封套娃、球员身高体重选拔、会议时间安排等。掌握这个套路,你可以应对各种类似的二维排序 + LIS 问题。

http://www.xdnf.cn/news/17898.html

相关文章:

  • Unity 实现逼真书本翻页效果
  • Vue响应式系统在超大型应用中的性能瓶颈
  • 深入浅出的 RocketMQ-面试题解析
  • 力扣hot100 | 普通数组 | 53. 最大子数组和、56. 合并区间、189. 轮转数组、238. 除自身以外数组的乘积、41. 缺失的第一个正数
  • LeetCode 面试经典 150_数组/字符串_最长公共前缀(20_14_C++_简单)(暴力破解)(求交集)
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘fairseq’问题
  • 关于Manus AI与多语言手写识别的技术
  • 学习笔记与效率提升指南:编程、记忆与面试备考
  • 中级统计师-会计学基础知识-第一章 账户与复试记账
  • diffusers学习--stable diffusion的管线解析
  • Cursor 分析 bug 记录
  • 楼宇自控系统是智能建筑核心,其重要地位日益凸显
  • C++面试——内存
  • Flutter 自定义组件开发指南
  • Spark03-RDD01-简介+常用的Transformation算子
  • 让数据可视化更简单:Embedding Atlas使用指南
  • initdata段使用方式
  • 第454题.四数相加II
  • Ant-Design AUpload如何显示缩略图;自定义哪些类型的数据可以使用img预览
  • 如何下载低版本的NVIDIA显卡驱动
  • Pytest项目_day17(随机测试数据)
  • 【LeetCode 热题 100】45. 跳跃游戏 II
  • 杭州网站建设:如何展示企业科研实力?
  • GitCode疑难问题诊疗
  • 状态流程框架(cola-component-statemachine)
  • 正点原子STM32H743配置 SDRAM
  • 序列晋升6:ElasticSearch深度解析,万字拆解
  • 【补充】数据库中有关系统编码和校验规则的简述
  • 非极大值抑制(NMS)详解:目标检测中的“去重神器”
  • 小兔鲜儿-小程序uni-app(二)