zoukankan      html  css  js  c++  java
  • 包含min函数的栈 & 滑动窗口最大值

    1.包含min函数的栈

    定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

     解析:

    对于普通栈的push()和pop()函数的复杂度为O(1);而获取最小值的min() 函数需要遍历整个栈,复杂度为 O(N)。

    本题的难点在于如何保证min()函数复杂度降维O(1)

    可借助辅助栈实现:

    数据栈 A : 栈 A 用于存储所有元素,保证入栈 push() 函数、出栈 pop() 函数、获取栈顶 top() 函数的正常逻辑。

    辅助栈 B : 栈 B 中存储栈 A 中所有 非严格降序 元素的子序列,则栈 A 中的最小元素始终对应栈 B 的栈顶元素。此时,min() 函数只需返回栈 B 的栈顶元素

    因此,只需设法维护好 栈 B 的元素,使其保持是栈 A 的非严格降序元素的子序列,即可实现 min() 函数的 O(1) 复杂度。

     函数设计:

      1.push(x)函数:对A push操作的同时也要对B push操作,重点保证栈B的元素非是非严格降序的

      • 执行元素x压入栈A
      • 栈B为空或x<=栈B的栈顶元素,则执行元素x压入栈B

      2.pop()函数:重点为保持栈A,栈B的元素一致性,始终让栈B中栈顶的元素是当前栈A中元素的最小值

      •  执行栈A元素出栈,将出栈元素记为y
      •  若y等于栈B的栈顶元素,则执行栈B元素出栈

      3.top() 函数: 直接返回栈 A 的栈顶元素,即返回 A.peek() ;

      4.min() 函数: 直接返回栈 B 的栈顶元素,即返回 B.peek() ;

    python

    class MinStack:
        def __init__(self):
            self.A, self.B = [], []
    
        def push(self, x: int) -> None:
            self.A.append(x)
            if not self.B or self.B[-1] >= x:
                self.B.append(x)
    
        def pop(self) -> None:
            if self.A.pop() == self.B[-1]:
                self.B.pop()
    
        def top(self) -> int:
            return self.A[-1]
    
        def min(self) -> int:
            return self.B[-1]
    

      Java

    class MinStack {
        Stack<Integer> A, B;
        public MinStack() {
            A = new Stack<>();
            B = new Stack<>();
        }
        public void push(int x) {
            A.add(x);
            if(B.empty() || B.peek() >= x)
                B.add(x);
        }
        public void pop() {
            if(A.pop().equals(B.peek()))
                B.pop();
        }
        public int top() {
            return A.peek();
        }
        public int min() {
            return B.peek();
        }
    }
    

    2.滑动窗口最大值

     解析:

    本题与包含min函数的栈这个题目的区别在于“出栈操作”删除的“列表尾部元素”“滑动窗口”删除的是“列表首部元素”

     

    本题使用 单调队列 即可解决以上问题。遍历数组时,每轮保证单调队列 deque :

    deque 内 仅包含窗口内的元素⇒ 每轮窗口滑动移除了元素 nums[i - 1],需将 deque内的对应元素一起删除。
    deque 内的元素 非严格递减 ⇒ 每轮窗口滑动添加了元素 nums[j + 1],需将 deque内所有 < nums[j + 1] 的元素删除。

     python

    class Solution:
        def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
            if not nums or k == 0:
                return []
            
            deque = collections.deque()
            # 未形成窗口前
            for i in range(k):
                while deque and deque[-1] < nums[i]:  # 保证队首是最大的
                    deque.pop()
                deque.append(nums[i])
    
            res = [deque[0]]
    
            # 形成窗口后
            for i in range(k, len(nums)):
                if deque[0] == nums[i-k]:  # 将队首等于已删除的元素去掉
                    deque.popleft()
                while deque and deque[-1] < nums[i]:
                    deque.pop()
                deque.append(nums[i])
                res.append(deque[0])
            return res
    

      Java

    // 单调队列的实现
    class MonotonicQueue {
        LinkedList<Integer> q = new LinkedList<>();
        public void push(int n) {
            // 将比n小的元素全部删除,保持队列的头是最大的
            while (!q.isEmpty() && q.getLast() < n) {
                q.pollLast();
            }
            // 将n加入尾部
            q.addLast(n);
        }
    
        // 前面的push方法已经保证了队列的头是最大的,因此获取最大值,只需获取队列的头部就行了
        public int max() {
            return q.getFirst();
        }
    
        // 删除队列头的元素,这里的删除,是遇到新加的数字与队列头的数字相同的情况
        public void pop(int n) {
            if (n == q.getFirst()) {
                q.pollFirst();
            }
        }
    }
    
    class Solution {
        public int[] maxSlidingWindow(int[] nums, int k) {
            MonotonicQueue window = new MonotonicQueue();
            // 集合
            List<Integer> res = new ArrayList<>();
    
            for (int i=0; i < nums.length; i++) {
                if (i < k-1) {
                    // 先填满窗口的前k-1
                    window.push(nums[i]);
                } else {
                    // 窗口向前滑动,加入新数字
                    window.push(nums[i]);
                    // 获取窗口中的最大值
                    res.add(window.max());
                    // 弹出旧的数字(这个旧数字如果与新的数字相同)
                    window.pop(nums[i-k+1]);
                }
            }
    
            // 返回值要求的是返回数组int[]
            int[] arr = new int[res.size()];
            for(int i=0; i < res.size(); i++) {
                arr[i] = res.get(i);
            }
    
            return arr;
    
        }
    }
    

      参考:

    https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58o46i/

  • 相关阅读:
    day01
    用表单验证数据(1)
    用表单验证数据
    表单
    ORM作业
    mysql完全卸载大全
    mycat特点及用途
    ajax 跨域请求解决方案
    myeclipse使用SVN团队开发
    配置mybatis错误总结
  • 原文地址:https://www.cnblogs.com/GumpYan/p/14472960.html
Copyright © 2011-2022 走看看