503.下一个更大元素 Ⅱ
思路分析
-
题目大意:在循环数组中,寻找当前数字
nums[i]
的下一个比他大的数字。 -
我的思路:
- 朴素想法:遍历数组中每一个元素
nums[i]
,再使用一层循环寻找比nums[i]
大的那个数字nums[j]
。可以将nums
数组中的数字 重复一遍 以减少其他操作。- 时间复杂度:
O(n^2)
。
- 时间复杂度:
- 为了减少时间复杂度。我想,可以寻找严格单调递减区间。比如栗子,
5 2 1 6
,严格单调减区间是5 2 1
,那么5 2 1
的数字都是6
。但是显然这种想法格局小了。因为可以轻松举出反例:5 2 1 4 6
,显然单调减区间5 2 1
的数字不能是4
,显然5
和4
的数字是6
,而2 1
的数字是4
。 - 然后,就没有思路了。用朴素想法写了一发,交上了。
- 朴素想法:遍历数组中每一个元素
-
使用 栈
- 考虑栗子
5 2 1 4 6
,5 2 1
是严格单调递减区间,直到碰到了一个比1
大的数字4
,此时,需要往前检索,直到找到第一个不比4
小的数字5
,4
和5
这个区间所有的数字(1 2
)都寻找到了比它们大的数字4
了,然后删除2 1
,5 4
继续组成一个严格单调递减区间。 - 上述思路的实现可以借助 栈 。
- 栈 维护这样一个严格单调递减区间,当遍历到数字
nums[i]
时,如果nums[i] < stack.top()
,那么将nums[i]
压到栈中;如果nums[i] >= stack.top()
,将栈顶元素弹出直到nums[i] <= stack.top()
或者栈空,然后将nums[i]
压入栈中。 - 显然,上述两种情况,无论如何都会将
nums[i]
压栈,因此,基本流程是:当遍历到nums[i]
时,将栈顶元素弹出直到nums[i] <= stack.top()
或者栈空,然后将nums[i]
入栈。 - 栈实现
- 这里还有一个小技巧,单调栈应该存放的是下标,这样可以简化操作。
- 显然,数组中的最大元素一定不回从栈中弹出。
- 实现循环数组,可以不用直接操作nums数组,可以只对下标取模即可。
- 考虑栗子
朴素想法
代码实现:
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
vector<int> ret(n, -1);
for (int i = 0; i < n; i ++){
nums.push_back(nums[i]);
}
for (int i = 0; i < n; i ++){
int now = nums[i];
for (int j = i + 1; j < 2*n; j ++){
if (nums[j] > now){
ret[i] = nums[j];
break;
}
}
}
return ret;
}
};
- 没想到竟然过了!
执行用时:204 ms, 在所有 C++ 提交中击败了11.42%的用户
内存消耗:23 MB, 在所有 C++ 提交中击败了33.30%的用户
栈实现
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
vector<int> ret(n, -1);
stack<int> st;
for (int i = 0; i < 2*n; i ++){
while (!st.empty() && nums[i % n] > nums[st.top()]){
ret[st.top()] = nums[i % n];
st.pop();
}
st.push(i % n);
}
return ret;
}
};
时间复杂度:O(n)
执行用时:44 ms, 在所有 C++ 提交中击败了72.22%的用户
内存消耗:22.6 MB, 在所有 C++ 提交中击败了66.55%的用户