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

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.dtcms.com/a/332647.html

相关文章:

  • 日本服务器租用选哪个机房国内访问比较快?
  • 【LINUX网络】HTTP协议基本结构、搭建自己的HTTP简单服务器
  • 企微用户部门同步HRS系统
  • 电脑上练打字用什么软件最好:10款打字软件评测
  • 滑窗|贪心
  • Sonatype Nexus Repository Manager docker版本安装
  • [优选算法专题二滑动窗口——无重复字符的最长子串]
  • Linux应用层开发--线程
  • react性能优化之useRef和useState
  • Nginx性能优化与安全配置:打造高性能Web服务器
  • Unity:PlayerPrefs笔记
  • 标准电子邮件地址格式(RFC 5322 里的 mailbox 语法)
  • ABAP : 内表/工作区转JSON
  • Prometheus 监控 Kubernetes Cluster 最新极简教程
  • FreeRTOS多核支持
  • 从根本上解决MAC权限问题(关闭sip)
  • 汽车后雾灯色度难达标?OAS 软件精准解决破瓶颈
  • 图论Day3学习心得
  • Chrome插件开发实战:从架构到发布全流程
  • Docker Compose部署Clickhouse最新版
  • 【软件设计模式】工厂方法与抽象工厂
  • 关于截屏时实现游戏暂停以及本地和上线不同步问题
  • pycharm2025导入anaconda创建的各个AI环境
  • C++第二十课:快递运费计算器 / 黑白配+石头剪刀布小游戏
  • 医院网络安全重保方案
  • 用 KNN 算法解锁分类的奥秘:从电影类型到鸢尾花开
  • 从电影分类到鸢尾花识别:KNN 算法实战指南
  • @[TOC](计算机是如何⼯作的) JavaEE==网站开发
  • MySQL 关键字总结,并结合 SQL 类型(DDL / DML / DQL / DCL / TCL) 说明每类关键字的作用、使用场景和示例
  • 华为实验综合小练习