单调栈及其应用

1.引言

前面我们已经讲过单调队列优先队列,这三者之间既有相似之处,也有各自的特点,没有看过的可以等下点击查看。

2.定义

单调栈(Monotonic Stack)是一种特殊的数据结构,其特点是栈中的元素保持单调递增或单调递减的状态。单调栈的核心思想是在栈中维护一个单调序列。具体来说,可以分为单调递增栈和单调递减栈。

  • 单调递增栈

    在单调递增栈中,栈顶元素总是栈中最小的元素。每当新元素加入栈时,只有当新元素大于栈顶元素时才会入栈,否则弹出栈顶元素直到满足条件为止。

  • 单调递减栈

    在单调递减栈中,栈顶元素总是栈中最大的元素。每当新元素加入栈时,只有当新元素小于栈顶元素时才会入栈,否则弹出栈顶元素 直到满足条件为止。

3.代码实例

单调栈最常用于寻找下一个更大/更小元素。

问题:要求找到数组中每个元素的下一个更大元素。如果没有更大的元素,则对应的位置填充 -1。

 import java.util.Stack;
 ​
 public class NextGreaterElement {
 ​
     public static int[] nextGreaterElement(int[] nums) {
         int n = nums.length;
         int[] result = new int[n];
         Stack<Integer> stack = new Stack<>();
 ​
         // 初始化结果数组为 -1
         for (int i = 0; i < n; i++) {
             result[i] = -1;
         }
         // 遍历数组
         for (int i = 0; i < n; i++) {
             // 当前元素大于栈顶元素时,弹出栈顶元素,并更新结果数组
             while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
                 int index = stack.pop();
                 result[index] = nums[i];
             }
             // 当前元素入栈
             stack.push(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 + " ");
         }
     }
 }

给定数组 [2, 1, 2, 4, 3],输出将是 [4, 2, 4, -1, -1]。这是因为:

  • 2 的下一个更大元素是 4

  • 1 的下一个更大元素是 2

  • 第二个 2 的下一个更大元素是 4

  • 43 没有更大的元素,因此它们的下一个更大元素都是 -1

4.进阶题

问题:

654. 最大二叉树 - 力扣(LeetCode)

给定一个不重复的整数数组 nums最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。

  2. 递归地在最大值 左边子数组前缀上 构建左子树。

  3. 递归地在最大值 右边子数组后缀上 构建右子树。

返回 nums 构建的 *最大二叉树*

提示:
  • 1 <= nums.length <= 1000

  • 0 <= nums[i] <= 1000

  • nums 中的所有整数 互不相同

题解:

这道题用可以用递归来解决,但会重复遍历数组来寻找最大值。我们观察到:题目要求可以简化为,找出每一个元素左侧和右侧第一个比它大的元素所在的位置。果左侧的元素较小,那么该元素就是左侧元素的右子节点;如果右侧的元素较小,那么该元素就是右侧元素的左子节点。这不就是典型的单调栈的应用吗?具体的思路可以看这篇题解,讲的非常详细。最后代码如下:

class Solution {
     public TreeNode constructMaximumBinaryTree(int[] nums) {
         Deque<TreeNode> deque = new ArrayDeque<>();
         for (int num : nums) {
             TreeNode node = new TreeNode(num);
             //果左侧的元素较小,那么该元素就是左侧元素的右子节点;
             //如果右侧的元素较小,那么该元素就是右侧元素的左子节点
             //通过单调栈的不断筛选找出每一个元素左侧和右侧第一个比它大的元素
             while (!deque.isEmpty() && num > deque.peekLast().val) {
                 node.left = deque.peekLast();
                 deque.pollLast();
             }
             if (!deque.isEmpty()) {
                 deque.peekLast().right = node;
             }
             deque.offerLast(node);
         }
         return deque.peekFirst();
     }
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值