JAVA:实现Next Grater Element下一个更大的元素算法(附带源码)
1. 项目背景详细介绍
在算法和数据结构的学习过程中,Next Greater Element(下一个更大的元素) 问题是 单调栈应用 的经典场景之一。它的基本描述是:
给定一个数组
arr,对于数组中的每个元素,找到它右边第一个 比它大的元素;如果不存在,则返回-1。
这是对「Next Smaller Element」问题的镜像版本。
示例:
输入数组:[4, 5, 2, 10, 8]
输出数组:[5, 10, 10, -1, -1]
解释:
-
对于
4,右边第一个比它大的元素是5; -
对于
5,右边第一个比它大的元素是10; -
对于
2,右边第一个比它大的元素是10; -
对于
10,右边没有更大的元素,因此返回-1; -
对于
8,右边没有更大的元素,因此返回-1。
应用场景
-
股票价格:判断什么时候价格会比当前高。
-
气温预测:寻找未来第一个比当前温度更高的日子。
-
数据分析:在序列中寻找下一个上升拐点。
-
单调栈应用:在「直方图最大矩形」和「雨水接雨」问题中非常常见。
2. 项目需求详细介绍
-
输入:一个整数数组
arr。 -
输出:一个整数数组
res,其中res[i]表示arr[i]的右边第一个比它大的元素。如果不存在,返回-1。 -
算法要求:
-
时间复杂度要求尽可能低(最好 O(n))。
-
使用 栈 辅助实现。
-
-
语言要求:使用 Java 实现。
-
提供完整的测试类,能运行并输出结果。
3. 相关技术详细介绍
3.1 暴力解法
-
对于数组中的每个元素
arr[i],从右边依次遍历,找到第一个比arr[i]大的元素。 -
如果找到,就作为答案;否则返回
-1。
复杂度:O(n²),效率低下。
3.2 单调栈解法(优化)
使用 单调栈(Monotonic Stack) 可以把复杂度降为 O(n)。
思路:
-
从右往左遍历数组;
-
使用一个 递减栈 来存储候选元素;
-
对于当前元素:
-
弹出栈顶中 小于等于当前值 的元素,因为它们不可能成为答案;
-
如果栈为空,则说明没有更大的元素,结果为
-1; -
如果栈顶比当前值大,则栈顶就是答案;
-
当前元素入栈,成为后续元素的候选。
-
4. 实现思路详细介绍
算法流程:
-
创建一个结果数组
res。 -
使用一个栈来保存右侧可能成为“下一个更大元素”的候选。
-
从右往左遍历:
-
弹出栈顶元素,直到栈顶比当前元素大;
-
如果栈为空,结果是
-1;否则栈顶就是答案; -
当前元素入栈。
-
-
返回结果数组。
时间复杂度:O(n),因为每个元素最多进栈和出栈一次。
空间复杂度:O(n),栈在最坏情况下可能存放所有元素。
5. 完整实现代码
import java.util.Stack;
import java.util.Arrays;
// =============================================
// Next Greater Element 算法类
// =============================================
class NextGreaterElement {
/**
* 计算数组中每个元素的下一个更大的元素
* @param arr 输入数组
* @return 结果数组
*/
public static int[] findNextGreaterElements(int[] arr) {
int n = arr.length;
int[] result = new int[n]; // 存放结果
Stack<Integer> stack = new Stack<>(); // 单调栈(递减栈)
// 从右往左遍历
for (int i = n - 1; i >= 0; i--) {
int current = arr[i];
// 弹出比当前元素小或等于的值
while (!stack.isEmpty() && stack.peek() <= current) {
stack.pop();
}
// 如果栈为空,说明右边没有更大的元素
result[i] = stack.isEmpty() ? -1 : stack.peek();
// 当前元素入栈
stack.push(current);
}
return result;
}
}
// =============================================
// 测试类 Main
// =============================================
public class Main {
public static void main(String[] args) {
int[] arr = {4, 5, 2, 10, 8};
System.out.println("输入数组: " + Arrays.toString(arr));
int[] result = NextGreaterElement.findNextGreaterElements(arr);
System.out.println("下一个更大元素数组: " + Arrays.toString(result));
}
}
6. 代码详细解读
-
核心方法
findNextGreaterElements-
输入:一个整型数组
arr; -
输出:对应的结果数组
result; -
使用 单调递减栈 存储候选元素;
-
从右往左遍历数组,每次保证栈顶始终是“比当前值大的候选元素”;
-
如果栈空,说明没有更大的元素,返回
-1; -
否则,栈顶即为下一个更大的元素。
-
-
栈的使用
-
栈中始终保持一个递减序列;
-
保证了 栈顶就是离当前最近的更大元素;
-
每个元素最多进出栈一次,总复杂度 O(n)。
-
-
Main 测试类
-
定义数组
[4, 5, 2, 10, 8]; -
调用
findNextGreaterElements方法; -
输出
[5, 10, 10, -1, -1]。
-
7. 项目详细总结
-
暴力解法 时间复杂度 O(n²),不适合大规模数据;
-
单调栈解法 时间复杂度 O(n),是最优解;
-
核心思想:通过栈维护一个候选集合,保证栈顶就是下一个更大的元素;
-
典型应用场景:
-
股票分析 → 找下一次价格上涨;
-
气温预测 → 找下一次升温;
-
数据处理 → 寻找趋势拐点;
-
算法题 → 「直方图最大矩形」、「每日温度」等题目都基于此思路。
-
8. 项目常见问题及解答
Q1:为什么要从右往左遍历?
答:因为我们要找的是“右边的元素”,从右往左遍历能保证栈中保存的都是右侧的候选值。
Q2:为什么栈中要弹出小于等于当前值的元素?
答:因为这些元素不可能成为当前值的“下一个更大元素”。
Q3:能否从左往右遍历?
答:可以,但实现逻辑会更复杂,一般建议从右往左。
Q4:如果要找“下一个更小元素”,如何修改?
答:只需要调整条件,把弹出逻辑改为“弹出大于等于当前值的元素”。
9. 扩展方向与性能优化
-
泛型化实现
-
可以扩展支持任意实现
Comparable接口的对象,例如字符串、浮点数等。
-
-
环形数组问题
-
在 LeetCode 上常见变体是 循环数组,即需要考虑数组首尾相接的情况,可以通过遍历两倍数组来解决。
-
-
双向扩展
-
不仅可以找右边的下一个更大元素,还可以找左边的下一个更大元素。
-
-
空间优化
-
如果允许覆盖原数组,可以直接在
arr中写结果,从而减少额外空间。
-
更多推荐


所有评论(0)