1673. 找出最具竞争力的子序列 - 力扣(LeetCode)
题目描述
给你一个整数数组
nums
和一个正整数k
,返回长度为k
且最具 竞争力 的nums
子序列。
数组的子序列是从数组中删除一些元素(可能不删除元素)得到的序列。
在子序列a
和子序列b
第一个不相同的位置上,如果a
中的数字小于b
中对应的数字,那么我们称子序列a
比子序列b
(相同长度下)更具 竞争力 。 例如,[1,3,4]
比[1,3,5]
更具竞争力,在第一个不相同的位置,也就是最后一个位置上,4
小于5
。
示例 1:
输入: nums = [3,5,2,6], k = 2
输出: [2,6]
解释: 在所有可能的子序列集合 {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中,[2,6] 最具竞争力。
题目分析
- 找到长度为k且最具竞争力的子序列,这个最具竞争是要相同元素最小。
- 那么我们来分析题意:
- 设n为数组长度,第一个元素一定是下标为
[0, n-k]
之间的最小值。因为要确保子序列长度为k。 - 设第一个元素下标为k,那么第二个元素一定是
[k+1, n-k+1]
的最小值。 - 以此类推。
- 设n为数组长度,第一个元素一定是下标为
- 上述题意是滑动窗口思想的,同时因为若第二个元素下标不是
n-k+1
,那么其一定比前者大。也刚好符合单调栈思想。因此我们可以用非递减(递增或相等)单调栈。 - 因此思路步骤应该为:
- 我们首先在下标范围
[0, n-k]
之间构建单调栈,然后取出栈中最小的元素也是最开始压入栈元素,那么栈肯定不符合要求,所以我们用双端队列即可。 - 取出最小元素后,将下标为
n-k+1
的元素压入双端队列中 - 然后再取出其中最小元素。
- 依次类推
- 我们首先在下标范围
代码
class Solution {
public:
vector<int> mostCompetitive(vector<int>& nums, int k) {
int n = nums.size();
deque<int> deq;
//将下标在[0, n-k]的元素压入非递减单调双端队列(思想和单调栈一样)
for(int i=0; i<=n-k; i++){
// while中>号控制的单调栈特性,>表示非递减,< 表示非递增 严格递增递减需要进一步设计。
while(!deq.empty()&&deq.back()>nums[i]){
deq.pop_back();
}
deq.push_back(nums[i]);
}
vector<int> ans;
//每次取一个元素,再将下标为n - k + ans.size()的元素压入单调双端队列中
while(!deq.empty()){
int curr = deq.front();
ans.push_back(curr);
deq.pop_front();
int index = n - k + ans.size();
if(index>=n) break;
while(!deq.empty()&&deq.back()>nums[index]){
deq.pop_back();
}
deq.push_back(nums[index]);
}
return ans;
}
};
优化1
上述为正常思路,同时其可以进行一些优化。
优化1
- 思想回顾: 之前通过题目分析后我们确定了两步走的思路,第一先将滑动窗口的值压入栈中,然后再将往后遍历,每次都取值,然后将滑动窗口往后,将窗口新包括的元素加入单调双端队列中。
- 优化前: 需要使用单调双端队列替代单调栈,在取值的时候,需要往双端队列中加入新的元素。
- 优化: 我们一次往单调栈中添加所有元素,不在取值的时候再进行添加操作,这样我们可以保证栈中的元素刚好k个,然后就可以直接用单调栈倒叙往数组中添加元素。
- 优化后: 可以直接使用单调栈, 一次添加元素。
- 但其实这两者时间复杂度没有差别。
class Solution {
public:
vector<int> mostCompetitive(vector<int>& nums, int k) {
int n = nums.size();
stack<int> sta;
for(int i=0; i<=n-1; i++){
//确保栈中元素加上剩余元素能超过k个,要不然最后子序列长度就不够了
while(!sta.empty()&&sta.top()>nums[i]&&sta.size()+n-i-k>0){
sta.pop();
}
//确保刚好到k个
if(sta.size()<k)
sta.push(nums[i]);
}
cout<<sta.size();
vector<int> ans(k, 0);
while(!sta.empty()){
int curr = sta.top();
ans[k-1] = curr;
k--;
sta.pop();
}
return ans;
}
};
优化2
单调栈问题常见优化,使用数组来表示单调栈。这样直接就添加元素后就可以直接进行输出了。
class Solution {
public:
vector<int> mostCompetitive(vector<int>& nums, int k) {
int n = nums.size();
vector<int> ans;
for(int i=0; i<=n-1; i++){
int index = ans.size();
while(index!=0&&ans[index-1]>nums[i]&&index+n-i-k>0){
ans.pop_back();
index = ans.size();
}
//确保刚好到k个
if(index < k)
ans.push_back(nums[i]);
}
return ans;
}
};