单调栈、单调队列刷题总结

单调栈介绍

单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。
听起来有点像堆(heap)?不是的,单调栈用途不太广泛,只处理一种典型的问题,叫做 Next Greater Element。

代码模板:

 public int[] nextGreaterElement(int[] nums) {
        int len = nums.length;
        int[] res = new int[len];
        Stack<Integer> s = new Stack<>();

        for (int i = len-1; i >= 0 ; i--) {
            while (!s.isEmpty() && s.peek()<=nums[i]) {
                s.pop();
            }
            res[i] = s.isEmpty()? -1:s.peek();
            s.push(nums[i]);
        }
        
        return res;
    }

单调栈典型例题

  1. 【力扣-496. 下一个更大元素 I】
    在这里插入图片描述
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int len1 = nums1.length, len2 = nums2.length;
        Map<Integer, Integer> map = new HashMap<>();
        int[] res = new int[len2];
        Stack<Integer> s = new Stack<>();

        for (int i = len2-1; i >= 0 ; i--) {
            while (!s.isEmpty() && s.peek()<=nums2[i]) {
                s.pop();
            }
            res[i] = s.isEmpty()? -1:s.peek();
            s.push(nums2[i]);
        }
        for (int i = 0; i < len2; i++) {
            map.put(nums2[i], res[i]);
        }
        int[] ans = new int[len1];
        for (int i = 0; i < len1; i++) {
            ans[i] = map.get(nums1[i]);
        }
        return ans;
    }
}

  1. 【力扣-503. 下一个更大元素 II】
    在这里插入图片描述
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int len = nums.length;
        Stack<Integer> stack = new Stack<>();
        int[] res = new int[len];

        for (int i = 2*len-1; i >= 0 ; i--) {
            while (!stack.isEmpty() && stack.peek() <= nums[i%len] ) {
                stack.pop();
            }
            res[i%len] = stack.isEmpty()? -1:stack.peek();
            stack.push(nums[i%len]);
        }

        return res;
    }
}

  1. 【力扣-739. 每日温度】
    在这里插入图片描述
class Solution {
    public int[] dailyTemperatures(int[] T) {
        int len = T.length;
        int[] res = new int[len];
        Stack<Integer> stack = new Stack<>();

        for (int i = len-1; i >= 0 ; i--) {
            while (!stack.isEmpty() && T[stack.peek()] <= T[i]) {
                stack.pop();
            }
            res[i] = stack.isEmpty()? 0:stack.peek()-i;
            stack.push(i);
        }

        return res;
    }
}

单调队列介绍

也许这种数据结构的名字你没听过,其实没啥难的,就是一个「队列」,只是使用了一点巧妙的方法,使得队列中的元素全都是单调递增(或递减)的。
「单调栈」主要解决 Next Great Number 一类算法问题,而「单调队列」这个数据结构可以解决滑动窗口相关的问题,比如说力扣第 239 题「滑动窗口最大值」,难度 Hard:

单调队列经典例题

  1. 【力扣-239. 滑动窗口最大值】
    在这里插入图片描述
class MonotonicQueue {
    LinkedList<Integer> list;

    public MonotonicQueue() {
        this.list = new LinkedList<>();
    }

    public void push(int num) {
        while (!list.isEmpty() && list.getLast() < num) {
            list.pollLast();
        }
        list.addLast(num);
    }

    public int getMax() {
        return list.getFirst();
    }

    public void pop(int num) {
        if (num == list.getFirst()) {
            list.pollFirst();
        }
    }
}
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        MonotonicQueue mq = new MonotonicQueue();
        List<Integer> al = new ArrayList<>();

        for (int i=0; i<nums.length; i++) {
            if (i < k-1) {
                mq.push(nums[i]);
            } else {
                mq.push(nums[i]);
                al.add(mq.getMax());
                mq.pop(nums[i+1-k]);
            }
        }

        int[] ans = new int[nums.length+1-k];
        for (int i = 0; i < ans.length; i++) {
            ans[i] = al.get(i);
        }
        return ans;
    }
}

总结:
这道题直观的解法是用优先队列,但是用单调队列可以在 O ( 1 ) O(1) O(1) 时间算出每个「窗口」中的最大值,使得整个算法在线性时间完成。

尝试用单调队列来做,这样的好处是单调队列插入、删除、获取最大值的时间复杂度
均是O(1),相对于用优先队列(大顶堆)来说更快!
击败从12%提升到了38%,还是少得可怜~
两次提交语法报错的原因分别在于:
1. push方法中,poll默认弹出头结点,所以这儿应该用pollLast
2. while (!list.isEmpty() && list.getLast() < num) 中为<,而不是<=

参考文献

单调栈结构解决三道算法题
单调队列结构解决滑动窗口问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,单调栈单调队列是两种不同的数据结构。单调栈是一个栈,它的特点是栈内的元素是单调的,可以是递增或递减的。在构建单调栈时,元素的插入和弹出都是在栈的一端进行的。与此类似,单调队列也是一个队列,它的特点是队列内的元素是单调的,可以是递增或递减的。在构建单调队列时,元素的插入是在队列的一端进行的,而弹出则是选择队列头进行的。 单调队列在解决某些问题时,能够提升效率。例如,滑动窗口最大值问题可以通过使用单调队列来解决。单调队列的结构可以通过以下代码来实现: ```python class MQueue: def __init__(self): self.queue = [] def push(self, value): while self.queue and self.queue[-1 < value: self.queue.pop(-1) self.queue.append(value) def pop(self): if self.queue: return self.queue.pop(0) ``` 上述代码定义了一个名为MQueue的类,它包含一个列表作为队列的存储结构。该类有两个方法,push和pop。push方法用于向队列中插入元素,它会删除队列尾部小于插入元素的所有元素,并将插入元素添加到队列尾部。pop方法用于弹出队列的头部元素。 总结来说,单调栈单调队列都是为了解决特定问题而设计的数据结构。单调栈在构建时元素的插入和弹出都是在栈的一端进行的,而单调队列则是在队列的一端进行的。在Python中,可以通过自定义类来实现单调队列的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值