354. 俄罗斯套娃信封问题

给你一个二维整数数组 envelopes ,其中 envelopes [i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组 “俄罗斯套娃” 信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。

示例 1:

输入:envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出:3
解释:最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。

示例 2:

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

提示

  • 1 <= envelopes.length <= 105
  • envelopes[i].length == 2
  • 1 <= wi, hi <= 105

# 解题思路 🤔

这道题是俄罗斯套娃信封问题,主要思路如下:

  1. 首先对信封按照宽度进行排序,如果宽度相同,则按照高度降序排序
  2. 将问题转化为在高度数组中寻找最长递增子序列 (LIS) 的问题
  3. 使用二分查找优化 LIS 的求解过程,时间复杂度从 O (n²) 优化到 O (nlogn)

# 代码实现 💻

// 主函数:计算最多能嵌套的信封数量
func maxEnvelopes(envelopes [][]int) int {
	n := len(envelopes)
	// 对信封进行排序:先按宽度升序,宽度相同时按高度降序
	sort.Slice(envelopes, func(i, j int) bool {
		if envelopes[i][0] == envelopes[j][0] {
			return envelopes[i][1] > envelopes[j][1]
		}
		return envelopes[i][0] < envelopes[j][0]
	})
	
	// 提取所有信封的高度,形成新的数组
	height := make([]int, n)
	for i := 0; i < n; i++ {
		height[i] = envelopes[i][1]
	}
	
	// 求解最长递增子序列
	return lengthOfLIS(height)
}
// 使用二分查找优化求解最长递增子序列
func lengthOfLIS(nums []int) int {
	// 存储每个牌堆顶部的牌
	top := make([]int,len(nums))
	// 牌堆数初始化为 0
	piles := 0 
	
	for i:=0; i<len(nums);i++{
		// 要处理的扑克牌
		poker := nums[i]
		// 搜索左侧边界的二分查找
		var left, right = 0,piles
		for left < right{
			mid := (left+right)/2
			if top[mid] > poker {
				right = mid
			}else if top[mid]< poker{
				left = mid + 1
			}else{
				right = mid
			}
		}
		// 没有找到合适的牌堆,新建一堆
		if left == piles{
			piles++
		}
		// 把这张牌放到牌堆顶
		top[left] = poker
	}
	// 牌堆数就是 LTS 长度
	return piles
}

# 复杂度分析 📊

  • 时间复杂度:O (nlogn),其中 n 是信封的数量
  • 空间复杂度:O (n),需要存储排序后的高度数组和牌堆数组

# 总结 💡

这道题的关键在于:

  1. 将二维问题转化为一维问题
  2. 使用排序和二分查找优化求解过程
  3. 理解 LIS 的二分查找解法
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

ZJM 微信支付

微信支付

ZJM 支付宝

支付宝