单调栈-leetcode-739. 每日温度

739. 每日温度

题目描述

暴力

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] res = new int[n];
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (temperatures[j] > temperatures[i]) {
                    res[i] = j - i;
                    break;
                }
            }
        }
        return res;
    }
}
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 时间复杂度: O ( 1 ) O(1) O(1) (忽略res数组)

单调栈

在求数组/队列中 左边(或右边)下一个最大值(或最小值)时,使用单调栈是一个非常不错的解法

复杂版

由于本题目是求数组中“右边第一个比当前元素大”的节点,所以这里采用的单调栈为:

  • 从栈顶到栈低 --> nums[index]单调递增
  • 栈中存的是temperatures下标i

思路:

  1. 遍历 t e m p e r a t u r e s temperatures temperatures 中所有元素,设当前下标为 i i i
  2. 若当前stack为空,则直接将 i i i入栈,就完事了;
  3. 否则,若以当前栈顶元素(由于栈中存放的是下标,其实是 t e m p e r a t u r e s [ s t a c k . p e e k ( ) ] temperatures[stack.peek()] temperatures[stack.peek()]<= 当前元素 t e m p e r a t u r e s [ i ] temperatures[i] temperatures[i],则不会破坏单调性,直接将 i 入栈,即可;
  4. 否则,若栈顶元素(同3解释) > 当前元素 t e m p e r a t u r e s [ i ] temperatures[i] temperatures[i],此时“单调性”被破坏,需要弹栈维护“单调性”
    • 若当前栈非空,且 temperatures[i] > temperatures[stack.peek(),则需要弹栈
    • 此时(单调性失效),当前元素 t e m p e r a t u r e s [ i ] temperatures[i] temperatures[i] 是栈中所有使得“单调性失效”元素的 右边第一个比它大的节点,则需要在弹栈时在 r e s res res 中记录之,即 res[index] = i - index;

两点注意

  1. 栈中存放的是下标 i i i,比较时应该是 t e m p e r a t u r e s [ i ] < = t e m p e r a t u r e s [ t o p ] temperatures[i] <= temperatures[top] temperatures[i]<=temperatures[top],而不是 t e m p e r a t u r e s [ i ] < = t o p temperatures[i] <= top temperatures[i]<=top
  2. 在弹栈维护单调性时(while循环中),循环条件必须为 temperatures[i] > temperatures[stack.peek()],而不是 temperatures[i] > temperatures[top]

    此时,弹栈后栈顶元素是动态变化的!!!

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        if (temperatures.length < 1) return new int[]{};
        int[] res = new int[temperatures.length];
        // 从栈顶到栈低 --> nums[index]单调递增
        // 栈中存的是temperatures下标i
        Deque<Integer> stack = new LinkedList<>();
        for (int i = 0; i < temperatures.length; i++) {
            if (stack.isEmpty()) {
                stack.push(i);
                continue;
            }
            int top = stack.peek();
            // 仍然(非严格)单调增,直接入栈
            if (temperatures[i] <= temperatures[top]) { // 注意:不是 <= top
                stack.push(i);
            } else { // >
                while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { // 不能temperatures[i] > top
                    int index= stack.pop();
                    // 记录
                    res[index] = i - index;
                }
                // 当前元素下标入栈
                stack.push(i);
            }
        }
        return res;
    }
}
  • 时间复杂度: O ( n ) O(n) O(n)
  • 时间复杂度: O ( n ) O(n) O(n) (忽略res数组)

可见单调栈其实是“用空间换时间”!

精简版

以上代码存在冗余部分,可精简如下:

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        if (temperatures.length < 1) return new int[]{};
        int[] res = new int[temperatures.length];
        // 单调栈:从栈顶到栈低 --> nums[index]单调递增,栈中存的是temperatures下标i
        Deque<Integer> stack = new LinkedList<>();
        for (int i = 0; i < temperatures.length; i++) {
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { // 不能temperatures[i] > top
                int index = stack.pop();
                res[index] = i - index;  // 记录
            }
            // 当前元素入栈
            stack.push(i); 
        }
        return res;
    }
}

时、空复杂度不变。

1019. 链表中的下一个更大节点

题目描述

单调栈

思路:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public int[] nextLargerNodes(ListNode head) {
        // 将链表转化为数组
        List<Integer> nums = new ArrayList<>();
        while (head != null) {
            nums.add(head.val);
            head = head.next;
        }
        System.out.println(nums);
        int n = nums.size();
        int[] res = new int[n];
        // 单调栈:从栈顶到栈低递增(非严格),栈中存放的是nums的下标
        Deque<Integer> stack = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (stack.isEmpty()) {
                stack.push(i);
                continue;
            }
            if (nums.get(i) <= nums.get(stack.peek())) {
                stack.push(i);
            } else {
                // 维护单调性(其中,栈中存放的是nums的下标)
                while (!stack.isEmpty() && nums.get(stack.peek()) < nums.get(i)) {
                    System.out.println("--------------");
                    System.out.println("stack = " + stack);
                    // 弹栈并记录
                    int index = stack.poll();
                    // 当前元素是单调栈中需要弹栈单调性的节点的右边第一个更大值
                    res[index] = nums.get(i);
                    System.out.println("i = " + i);
                    System.out.println("index = " + index);
                }
                // 当前元素入栈(保证单调增)
                stack.push(i);
            }
        }
        return res;
    }
}

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值