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 中单调栈的实现与应用,涵盖了"下一个更大元素"和"股票价格跨度"两个经典案例。无论你是准备算法面试,还是在项目中优化代码,单调栈都值得你深入掌握。

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

参考资料


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

相关推荐
demonlg011221 分钟前
Jenkins 共享库(Shared Libraries)使用说明文档
java·运维·python·jenkins·运维开发·devops
测试老哥22 分钟前
白盒测试用例的设计(图文讲解)
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
藍海琴泉25 分钟前
Python虚拟环境:从入门到实战指南
开发语言·python
Elastic 中国社区官方博客26 分钟前
Elasticsearch:构建 AI 驱动的搜索体验
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
码熔burning30 分钟前
Spring Boot 日志深度解析:从入门到精通
java·spring boot·后端·日志
cfjybgkmf37 分钟前
Python字典
开发语言·python
bing_15838 分钟前
JVM 类加载器之间的层次关系,以及类加载的委托机制
java·jvm
计算机-秋大田38 分钟前
基于Spring Boot的智能停车计费系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
1haooo39 分钟前
API-Arrays
java·数据结构·算法
ManRock40 分钟前
wyq01
python