leetcode
leetcode 451 ~ 500
下一个更大元素 II

下一个更大元素 II

难度:

标签:

题目描述

给定一个循环数组 nums ( nums[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 <= 104
  • -109 <= nums[i] <= 109

代码结果

运行时间: 256 ms, 内存: 16.5 MB


/*
 * Solution Idea with Java Streams:
 * 1. Create an array of result and initialize to -1.
 * 2. Use IntStream to simulate iterating twice over the array.
 * 3. For each element, check if there's a larger element in the rest of the array.
 * 4. Use a Stack to store indices and find the next greater element similarly to the Java solution.
 */
 
import java.util.Arrays;
import java.util.Stack;
import java.util.stream.IntStream;
 
public class NextGreaterElementStream {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        int[] result = new int[n];
        Arrays.fill(result, -1);
        Stack<Integer> stack = new Stack<>();
 
        IntStream.range(0, 2 * n).forEach(i -> {
            while (!stack.isEmpty() && nums[stack.peek()] < nums[i % n]) {
                result[stack.pop()] = nums[i % n];
            }
            if (i < n) {
                stack.push(i);
            }
        });
 
        return result;
    }
}
 

解释

方法:

这个题解使用单调栈的思路来解决问题。首先将原数组复制一遍拼接到原数组后面,模拟循环数组。然后遍历拼接后的数组,对于每个元素,将栈中所有小于当前元素的元素弹出,并将当前元素作为它们的下一个更大元素。最后将当前元素的索引压入栈中。遍历结束后,取ans数组的前n个元素作为最终结果返回。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中提到的单调栈是什么样的数据结构,它是如何帮助解决这个问题的?
单调栈是一种特殊的栈,它在操作过程中会保持元素的单调性(递增或递减)。在这个题解中,使用的是一个单调递减栈,这意味着栈顶到栈底的元素是递减的。这种数据结构对于解决“下一个更大元素”类问题特别有效,因为它可以追踪已遍历元素中未找到更大元素的索引,并快速确定新元素的加入是否可以成为之前某些元素的“下一个更大元素”。当遇到一个新元素时,如果它比栈顶元素大,则栈顶元素的下一个更大元素就是这个新元素,接着将栈顶元素弹出,并继续此过程直到栈顶元素大于或等于当前元素或栈为空,然后将当前元素的索引压入栈,以供后续使用。
🦆
为什么要将原数组复制并拼接到自己的后面来模拟循环数组,而不是直接在原数组上进行循环操作?
在循环数组中,数组的末尾元素后面是数组的开头元素,这种循环关系在普通的线性数组中不易处理特别是在索引管理上。通过将原数组复制并拼接到其后面,我们可以通过线性遍历(即遍历这个扩展的数组)的方式简化问题的复杂性,使得每个元素都可以直接找到其后面的元素而无需考虑边界。此外,这种方法也方便使用单调栈处理,因为我们可以一次性地处理所有的关系而无需在数组末尾再进行额外的循环判断和索引调整。
🦆
在题解的代码实现中,为什么要初始化结果数组ans的长度为2n而不是n?
因为题解中将原数组复制并拼接到了自己的后面,所以新的数组长度变为了2n。在遍历这个长度为2n的数组时,我们需要记录每个位置的下一个更大元素,即使是复制的部分。虽然最终只需要原始数组长度n的结果,初始化长度为2n是为了在处理过程中能够存储所有可能的结果,确保不会因为索引超出范围而导致错误。遍历结束后,我们只取结果数组的前n个元素作为最终答案。
🦆
如果数组已经是降序排列,单调栈的行为会有什么变化?
如果数组是降序排列的,每个元素都比前一个元素小,那么在使用单调递减栈的情况下,这些元素会依次被压入栈中,因为没有任何一个元素能弹出栈中的元素(没有找到更大的元素)。这将导致栈中元素逐渐增加,直到处理完所有元素。在整个过程中,栈将保存所有元素的索引,因为栈中的每个元素都没有找到比它大的下一个元素,所以结果数组中对应位置都将保持初始化的默认值(如-1)。

相关问题

下一个更大元素 I

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j]下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素

 

示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

 

提示:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 104
  • nums1nums2中所有整数 互不相同
  • nums1 中的所有整数同样出现在 nums2

 

进阶:你可以设计一个时间复杂度为 O(nums1.length + nums2.length) 的解决方案吗?

下一个更大元素 III

给你一个正整数 n ,请你找出符合条件的最小整数,其由重新排列 n 中存在的每位数字组成,并且其值大于 n 。如果不存在这样的正整数,则返回 -1

注意 ,返回的整数应当是一个 32 位整数 ,如果存在满足题意的答案,但不是 32 位整数 ,同样返回 -1

 

示例 1:

输入:n = 12
输出:21

示例 2:

输入:n = 21
输出:-1

 

提示:

  • 1 <= n <= 231 - 1