下一个更大的元素

这是我碰到的一道非常经典的单调栈的题目,题目中考察的正是单调栈的特性及用法,可以一起学习一下。

题目描述

给定一个循环数组 numsnums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums_ 中每个元素的 _下一个更大元素

数字 x下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1

示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释:

第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

示例 2:

输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]

提示:

  • 1 <= nums.length <= 10(4)
  • -10(9) <= nums[i] <= 10(9)

解析 1

何为单调栈,即栈中栈底到栈顶存储的数字是单调的,而在本题中栈中存放的数组中对应的下标,这些下标所对应的数组中的数字在栈中是单调不升的。

总体思路为:

  • 因为数组为循环数组,所以要遍历两边数组
  • 如果栈不为空且栈顶元素小于等于当前元素,将弹出栈顶元素,并将弹出元素的下个最大元素设为当前元素
  • 并将当前元素下标压栈
  • 重复以上过程

遍历数组时,当前元素为 nums[i],当前栈顶元素值小于该数组元素时,栈顶元素的下个更大值即为当前元素 nums[i]。只要遍历到比栈顶元素值更大的数,就意味着栈顶元素找到了答案,记录答案,然后弹出栈顶。

参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;

int[] res = new int[n];
Arrays.fill(res, -1);

Deque<Integer> stack = new LinkedList<>();
for(int i = 0; i < 2 * n - 1; i++){
while(!stack.isEmpty() && nums[stack.peek()] < nums[i % n]){
res[stack.pop()] = nums[i % n];
}
stack.push(i % n);
}
return res;
}

时间复杂度:

O(n)

空间复杂度:

O(n)

解析 2

解析 1 中是从左至右遍历数组中的元素的,还有一种自右向左遍历的思路,总体算法思路相似,细节略有不同。

自右向左遍历,栈中记录的是下一个更大元素的 次选

参考 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
int[] ans = new int[n];

Arrays.fill(ans, -1);
Deque<Integer> stack = new LinkedList<>();
for (int i = 2 * n - 1; i >= 0; i--) {
int x = nums[i % n];
while (!stack.isEmpty() && x >= stack.peek()) {
// 由于 x 的出现,栈顶元素永远不会是左边元素的「下一个更大元素」
stack.pop();
}
if (i < n && !stack.isEmpty()) {
ans[i] = stack.peek();
}
stack.push(x);
}
return ans;
}

时间复杂度:

O(n)

空间复杂度:

O(n)

相关链接

503. 下一个更大元素 II - 力扣(LeetCode)