俄罗斯套娃信封问题
难度:
标签:
题目描述
给你一个二维整数数组 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
代码结果
运行时间: 164 ms, 内存: 0.0 MB
/*
题目思路:
1. 仍然是按照宽度升序,宽度相同高度降序排序。
2. 使用Java Stream进行排序和处理。
3. 使用LIS方法求解高度的最长递增子序列。
*/
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class Solution {
public int maxEnvelopes(int[][] envelopes) {
if (envelopes == null || envelopes.length == 0 || envelopes[0].length != 2) {
return 0;
}
// 使用Java Stream进行排序
List<int[]> sortedEnvelopes = Arrays.stream(envelopes)
.sorted((a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0])
.collect(Collectors.toList());
int n = sortedEnvelopes.size();
int[] dp = new int[n];
int len = 0;
for (int[] envelope : sortedEnvelopes) {
int index = Arrays.binarySearch(dp, 0, len, envelope[1]);
if (index < 0) {
index = -(index + 1);
}
dp[index] = envelope[1];
if (index == len) {
len++;
}
}
return len;
}
}
解释
方法:
这个题解的思路是先按宽度升序和高度降序对信封进行排序,然后对排序后的信封高度数组求最长上升子序列(LIS)。这样做的原因是,对于宽度相同的信封,我们只需保留高度最小的,因为其他的信封都可以套在高度最小的信封外面。排序后,问题就转化为在高度数组上求LIS,而LIS可以通过动态规划和二分查找求解。
时间复杂度:
O(nlogn)
空间复杂度:
O(n)
代码细节讲解
🦆
为什么在排序信封时选择先按宽度升序然后按高度降序排序?
▷🦆
在宽度相同的情况下,按高度降序排列是否意味着我们在处理宽度相同的信封时只考虑最高的一个?
▷🦆
LIS函数中,dp数组的每个元素表示什么?为什么dp数组的更新过程可以保证找到最长上升子序列?
▷🦆
二分查找在LIS函数中的作用是什么?它如何帮助优化整个求解过程?
▷相关问题
最长递增子序列
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3] 输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7] 输出:1
提示:
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
进阶:
- 你能将算法的时间复杂度降低到
O(n log(n))
吗?