题目链接:下一个更大元素
题干解读:
题意很简单,给你一个数组nums,你需要返回一个数组ans,其中ans[i]为nums[i]后方第二个比nums[i]的数,若不存在,则ans[i]为-1;
解题思路:
这个题需要用到单调栈的概念,考虑一个更简单的情况:如果我们只需要获取每个数后面第一个比它大的数,则我们只需要用一个单调栈st对数组下标进行维护,对数组进行遍历:
(1)若当前遍历到的数nums[i]大于栈顶对应的数nums[st.top()],则栈顶的下标出栈st.pop(),该第一个比该下标处大的数即当前遍历到的数ans[st.top()]=nums[i],重复该步骤;
(2)将当前遍历到的下标i压入栈中。
而对于该题,我们需要获取第二个大的数,也就是说我们可以把数组中每个数都想象成有两条命,当有遍历到有一个数大于他,则扣除一条命,当它的命扣完时,将它的命扣完的那个数就是第二个比他打的数。和上述步骤相似,我们用一个单调栈st对有两条命的数的下标进行维护,采用一个小顶堆q对有一条命的数的值(需要借助值进行小顶堆排序)和下标进行维护,遍历数组nums:
(1)若nums[i]大于堆顶的数的值,则nums[i]就是堆顶处这个数的第二大数字,堆顶数字出队,重复该步骤,直到堆为空或者nums[i]不大于堆顶处的数。
(2)若nums[i]大于栈顶的下标对应的数,则该数扣除一条命,将该数及其下标加入小顶堆,栈顶出栈,重复该步骤,直至栈为空或nums[i]不大于栈顶处的数。
(3)将当前遍历到的数的下标压入栈中。
这个思路可以扩充至找第n个大的数。
同时这个题中可以发现进行步骤(1)后堆中最小的数>nums[i],而进行(2)时会将栈中所有小于nums[i]的数都压入堆中(小的数先入堆),因此只要保证第(2)步中将数压入堆的时候保持栈中的顺序,即可以采用一个单调栈来代替堆(无需进行额外的排序,减小时间复杂度)
class Solution {
public:
vector<int> secondGreaterElement(vector<int>& nums) {
stack<int> st;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
vector<int> ans(nums.size(), - 1);
for(int i = 0; i < nums.size(); i ++){
while(!q.empty() && nums[i] > q.top().first){
ans[q.top().second] = nums[i];
q.pop();
}
while(!st.empty() && nums[i] > nums[st.top()]){
q.push({nums[st.top()], st.top()});
st.pop();
}
st.push(i);
}
return ans;
}
};