Java 之「单调栈」:从入门到实战

Java 单调栈:从入门到实战

文章目录

引言

在 Java 编程中,数据结构的选择和使用往往是解决复杂问题的关键。单调栈(Monotonic Stack)作为一种高效的数据结构,能够在 O(n) 时间复杂度内解决许多与单调性相关的问题,例如"下一个更大元素"、"股票价格跨度"等。对于 CSDN 的读者来说,深入理解单调栈不仅能提升代码能力,还能在面试和项目中脱颖而出。本文将带你从单调栈的基本概念入手,逐步深入到 Java 实现与实战应用,附上详细代码示例,让你轻松掌握这一利器!


什么是单调栈?

单调栈是一种特殊的栈结构,其核心在于保持栈内元素的单调性,即从栈底到栈顶元素要么单调递增,要么单调递减。在操作时,单调栈会通过弹出不符合单调性的元素来维持这一特性,从而在处理实时单调性问题时表现出色。

单调递增栈

  • 定义:栈内元素从栈底到栈顶单调递增。
  • 入栈规则:当新元素入栈时,如果栈顶元素大于或等于新元素,则不断弹出栈顶元素,直到栈顶小于新元素或栈为空。

单调递减栈

  • 定义:栈内元素从栈底到栈顶单调递减。
  • 入栈规则:当新元素入栈时,如果栈顶元素小于或等于新元素,则不断弹出栈顶元素,直到栈顶大于新元素或栈为空。

单调栈的这种动态调整机制,使其在特定场景下非常高效。


单调栈的应用场景

单调栈在算法和实际项目中有广泛应用,以下是几个经典场景:

  1. 下一个更大元素(Next Greater Element):在数组中为每个元素找到右边第一个比它大的元素。
  2. 股票价格跨度(Stock Span Problem):计算连续天数中股票价格不高于当天的最大天数。
  3. 直方图中最大矩形(Largest Rectangle in Histogram):在直方图中找到面积最大的矩形。
  4. 温度预测:在温度数组中找到每个温度下一次更高温度出现的日子。

这些问题有一个共同点:需要快速找到某种单调关系,而单调栈正是解决这类问题的"杀手锏"。


Java 实现单调栈

在 Java 中,我们可以利用 java.util.Stack 类来实现单调栈。下面以"下一个更大元素"问题为例,展示单调递减栈的实现。

代码示例:下一个更大元素

java 复制代码
import java.util.Stack;

public class MonotonicStackExample {
    public static int[] nextGreaterElement(int[] nums) {
        int n = nums.length;
        int[] result = new int[n];
        Stack<Integer> stack = new Stack<>(); // 单调递减栈

        // 从右向左遍历数组
        for (int i = n - 1; i >= 0; i--) {
            // 弹出所有小于当前元素的栈内元素
            while (!stack.isEmpty() && stack.peek() <= nums[i]) {
                stack.pop();
            }
            // 如果栈不为空,栈顶即为下一个更大元素,否则为 -1
            result[i] = stack.isEmpty() ? -1 : stack.peek();
            // 当前元素入栈
            stack.push(nums[i]);
        }
        return result;
    }

    public static void main(String[] args) {
        int[] nums = {2, 1, 2, 4, 3};
        int[] result = nextGreaterElement(nums);
        for (int num : result) {
            System.out.print(num + " "); // 输出: 4 2 4 -1 -1
        }
    }
}
代码解析
  • 栈的单调性:栈内元素保持单调递减。
  • 遍历方向:从右向左遍历,便于找到右侧的更大元素。
  • 逻辑 :
    1. 对于当前元素 nums[i],弹出栈内所有小于等于它的元素。
    2. 若栈为空,则右侧无更大元素,记为 -1;否则栈顶即为答案。
    3. 将当前元素压入栈,继续处理下一个元素。

单调栈的优势

  1. 时间复杂度:每个元素最多入栈和出栈一次,总时间复杂度为 O(n)。
  2. 空间复杂度:最坏情况下栈存储所有元素,空间复杂度为 O(n)。
  3. 实时性:单调栈适合动态数据流场景,能实时维护单调性。

实战应用:股票价格跨度

问题描述:给定一个股票价格数组,计算每一天股票价格不高于当天的连续天数(包括当天)。

解法:使用单调递增栈,栈内存储价格及其对应的跨度,动态累加跨度。

代码示例

java 复制代码
import java.util.Stack;

public class StockSpan {
    public static int[] stockSpan(int[] prices) {
        int n = prices.length;
        int[] spans = new int[n];
        Stack<int[]> stack = new Stack<>(); // 存储 [价格, 跨度]

        for (int i = 0; i < n; i++) {
            int span = 1;
            // 弹出栈内小于等于当前价格的元素,累加其跨度
            while (!stack.isEmpty() && stack.peek()[0] <= prices[i]) {
                span += stack.pop()[1];
            }
            spans[i] = span;
            stack.push(new int[]{prices[i], span});
        }
        return spans;
    }

    public static void main(String[] args) {
        int[] prices = {100, 80, 60, 70, 60, 75, 85};
        int[] spans = stockSpan(prices);
        for (int span : spans) {
            System.out.print(span + " "); // 输出: 1 1 1 2 1 4 6
        }
    }
}
代码解析
  • 栈的单调性:栈内价格单调递增。
  • 跨度计算:当遇到更高价格时,弹出栈内较小的价格并累加其跨度。
  • 结果 :spans[i] 表示第 i 天对应的跨度。

总结

单调栈凭借其高效性和简洁性,成为解决单调性问题的利器。本文从概念到代码,详细介绍了 Java 中单调栈的实现与应用,涵盖了"下一个更大元素"和"股票价格跨度"两个经典案例。无论你是准备算法面试,还是在项目中优化代码,单调栈都值得你深入掌握。

希望这篇博客能为你带来启发!如果有疑问或想了解更多应用场景,欢迎在评论区留言交流。

参考资料


希望这篇博客能帮你在面试或项目中游刃有余!有疑问欢迎留言讨论,喜欢请点赞关注哦!

相关推荐
一水鉴天3 小时前
映射、哈希表与哈斯图:计算机科学的三种基线 20261003(元宝)
开发语言·人工智能
Frank_refuel3 小时前
C++11之一场名为“搬家”的 C++ 之旅
开发语言·c++
海绵宝宝转agent4 小时前
learn-claude-code第1-5章开源学习笔记分享
人工智能·笔记·python·学习
小奇不哭4 小时前
ROS2 单节点,RGB-D 深度相机局部路径提取 + B 样条平滑路径 + YOLOv8 障碍物检测 + 深度测距,用于移动机器人视觉局部循迹。
python·yolov8·ros2·cv2·路径探索循迹
小小龙学IT4 小时前
Python scikit-learn 机器学习库深度解析
python·机器学习·scikit-learn
Wang's Blog4 小时前
Java 项目实战: 外卖平台优化-Nginx配置文件结构与块层级
java·开发语言·nginx
Elastic 中国社区官方博客4 小时前
将你自己的密钥用于现有 Elastic Cloud 部署
大数据·数据库·elasticsearch·全文检索
2601_962071575 小时前
类变量和全局变量的查找路径有什么区别?
开发语言·python
红海云5 小时前
Jev:给智能系统做判断的模型
大数据·数据库·人工智能
卷无止境5 小时前
独立开发者的"富矿地带":哪些垂直领域值得你押注一辈子?
后端·python