1. 题目

  1. https://leetcode-cn.com/problems/valid-parentheses/
  2. https://leetcode-cn.com/problems/min-stack/
  3. https://leetcode-cn.com/problems/largest-rectangle-in-histogram
  4. https://leetcode-cn.com/problems/sliding-window-maximum
  5. https://leetcode-cn.com/problems/design-circular-deque
  6. https://leetcode-cn.com/problems/trapping-rain-water/

2.2 最小栈#155(简单)

https://leetcode-cn.com/problems/min-stack/

  1. class MinStack {
  2. //解法一:辅助栈+数栈。辅助栈存最小值,数栈存数就好了.
  3. Deque<Integer> xStack;
  4. Deque<Integer> minStack;
  5. public MinStack() {
  6. this.xStack = new LinkedList<Integer>();
  7. this.minStack = new LinkedList<Integer>();
  8. minStack.push(Integer.MAX_VALUE);
  9. }
  10. public void push(int val) {
  11. xStack.push(val);
  12. minStack.push(Math.min(val, minStack.peek()));
  13. }
  14. public void pop() {
  15. xStack.pop();
  16. minStack.pop();
  17. }
  18. public int top() {
  19. return xStack.peek();
  20. }
  21. public int getMin() {
  22. return minStack.peek();
  23. }
  24. }
  25. /**
  26. * Your MinStack object will be instantiated and called as such:
  27. * MinStack obj = new MinStack();
  28. * obj.push(val);
  29. * obj.pop();
  30. * int param_3 = obj.top();
  31. * int param_4 = obj.getMin();
  32. */

2.4 滑动窗口最大值#239(困难)

https://leetcode-cn.com/problems/sliding-window-maximum

  1. public int[] maxSlidingWindow(int[] nums, int k) {
  2. //该解法超时了❌
  3. //方法一:暴力解法.时间复杂度O(n^2),空间复杂度O(1)
  4. int length = nums.length;
  5. int n = length - k + 1;
  6. int[] ans = new int[n];
  7. for (int i = 0; i <= length - k; ++i) {
  8. ans[i] = nums[i];
  9. for (int j = i; j < i + k; ++j) {
  10. if (nums[j] > ans[i]) {
  11. ans[i] = nums[j];
  12. }
  13. }
  14. }
  15. return ans;
  16. }
  17. public int[] maxSlidingWindow(int[] nums, int k) {
  18. //方法一:优先队列.时间复杂度O(nlogn),空间复杂度O(n)
  19. //涉及到的Java中PriorityQueue的api有:
  20. //peek()查看队列中第一个元素。element()方法会抛异常,前者不会。
  21. //offer()往队首插入元素。add()会抛异常,前者不会。
  22. //poll()获取队首元素并删除。remove()会抛异常,前者不会。
  23. int length = nums.length;
  24. //队列中存储的(nums[i], index)这样的二元组
  25. PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
  26. public int compare(int[] pair1, int[] pair2) {
  27. //排序规则是:从大到小,同样大则下标大的大
  28. //这里可以写成lambda表达式
  29. return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
  30. }
  31. });
  32. //初始化优先队列,先放k个数进去
  33. for (int i = 0; i < k; ++i) {
  34. queue.offer(new int[]{nums[i], i});
  35. }
  36. int[] ans = new int[length - k + 1];
  37. //目前为止的优先队列的第一个元素一定是这k个元素中的最大值。因为从大到小的顺序。
  38. //另外[0]这个位置存放的是nums[i]元素,[1]这个位置存放的是下标。
  39. ans[0] = queue.peek()[0];
  40. for (int i = k; i < length; ++i) {
  41. queue.offer(new int[]{nums[i], i});
  42. while (queue.peek()[1] <= i - k) {
  43. //比较队列中的第一个元素的【下标】是否还在滑动窗口中,不在则出队
  44. queue.poll();
  45. }
  46. //堆顶的元素自然而然就是最大的元素
  47. ans[i - k + 1] = queue.peek()[0];
  48. }
  49. return ans;
  50. }
  51. public int[] maxSlidingWindow(int[] nums, int k) {
  52. //解法三:单调队列。时间复杂度O(n),空间复杂度O(k)
  53. int n = nums.length;
  54. Deque<Integer> deque = new LinkedList<>();
  55. //先初始化k个元素,这个时候窗口还没开始滑动。
  56. //这里也能找到最开始的k个元素中的最大值。
  57. for (int i = 0; i < k; ++i) {
  58. //如果队列不为空并且当前元素大于队尾元素,则让队尾元素出队直到条件不满足
  59. while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
  60. deque.pollLast();
  61. }
  62. //将下标插入到队尾。因为通过下标查找元素比较容易,反之麻烦。
  63. deque.offerLast(i);
  64. }
  65. //一共有n-k+1个元素
  66. int[] ans = new int[n - k + 1];
  67. //此时的队首元素一定是第一个滑动窗口中的最大值。
  68. ans[0] = nums[deque.peekFirst()];
  69. for (int i = k; i < n; ++i) {
  70. //做法同上一个循环,就不再赘述
  71. while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
  72. deque.pollLast();
  73. }
  74. deque.offerLast(i);
  75. //这个循环是为了检查队首的元素是否处于滑动窗口中,不存在则将队首元素出队
  76. while (deque.peekFirst() <= i - k) {
  77. deque.pollFirst();
  78. }
  79. ans[i - k + 1] = nums[deque.peekFirst()];
  80. }
  81. return ans;
  82. }
  83. public int[] maxSlidingWindow(int[] nums, int k) {
  84. //解法三:单调队列优化写法。时间复杂度O(n),空间复杂度O(k)
  85. if (nums == null || nums.length < 2) {
  86. return nums;
  87. }
  88. int n = nums.length;
  89. //用双端队列并且按照从大到小的顺序存放当前滑动窗口中值最大元素的下标。
  90. Deque<Integer> deque = new LinkedList<>();
  91. //答案数组咯
  92. int[] ans = new int[n - k + 1];
  93. //没必要循环到n
  94. for (int i = 0; i < n; ++i) {
  95. //保证这个队列依然是从大到小的顺序,否则将队尾小于当前元素的数出队。
  96. //直至队列为空或者满足从大到小的顺序。
  97. while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
  98. deque.pollLast();
  99. }
  100. //将当前元素的下标添加到队尾
  101. deque.offerLast(i);
  102. //判断队首元素是否处于滑动窗口范围内。一次只入队一个,所以只需要检查一次
  103. if (deque.peekFirst() <= i - k) {
  104. deque.pollFirst();
  105. }
  106. //判断窗口长度是否足够k长。因为把初始化操作合并到一起了,
  107. //所以最开始没有k长的时候要拿出来单独讨论。
  108. //足够长就把队首元素放入答案数组,因为队首元素最大。
  109. if (i - k + 1 >= 0) {
  110. ans[i - k + 1] = nums[deque.peekFirst()];
  111. }
  112. }
  113. return ans;
  114. }

2.5 设计循环双端队列#641(中等)

https://leetcode-cn.com/problems/design-circular-deque

class MyCircularDeque {

    /** Initialize your data structure here. Set the size of the deque to be k. */
    private int capacity;
    private int[] arr;
    private int front;
    private int rear;

    public MyCircularDeque(int k) {
        //要多留一个位置来区分空和满
        capacity = k + 1;
        arr = new int[capacity];
        //头指针指向第一个存放元素的位置
        front = 0;
        //尾指针指向下一个插入元素的位置。
        rear = 0;
    }

    /** Adds an item at the front of Deque. Return true if the operation is successful. */
    public boolean insertFront(int value) {
        //3.接下来的就随便写就好了没有顺序要求了。
        if (isFull()) {
            return false;
        }
        //循环队列记得-1时要加capacity再取余,防止下标出现负数。
        //而+1时不需要加capacity直接取余即可。
        front = (front - 1 + capacity) % capacity;
        arr[front] = value;
        return true;
    }

    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    public boolean insertLast(int value) {
        if (isFull()) {
            return false;
        }
        arr[rear] = value;
        rear = (rear + 1) % capacity;
        return true;
    }

    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    public boolean deleteFront() {
        if (isEmpty()) {
            return false;
        }
        front = (front + 1) % capacity;
        return true;
    }

    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    public boolean deleteLast() {
        if (isEmpty()) {
            return false;
        }
        rear = (rear - 1 + capacity) % capacity;
        return true;
    }

    /** Get the front item from the deque. */
    public int getFront() {
        if (isEmpty()) {
            return -1;
        }
        return arr[front];
    }

    /** Get the last item from the deque. */
    public int getRear() {
        if (isEmpty()) {
            return -1;
        }
        return arr[(rear - 1 + capacity) % capacity];
    }

    /** Checks whether the circular deque is empty or not. */
    public boolean isEmpty() {
        //1.先写判空和判满
        return front == rear;
    }

    /** Checks whether the circular deque is full or not. */
    public boolean isFull() {
        //2.再写判满
        return (rear + 1) % capacity == front;
    }
}

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque obj = new MyCircularDeque(k);
 * boolean param_1 = obj.insertFront(value);
 * boolean param_2 = obj.insertLast(value);
 * boolean param_3 = obj.deleteFront();
 * boolean param_4 = obj.deleteLast();
 * int param_5 = obj.getFront();
 * int param_6 = obj.getRear();
 * boolean param_7 = obj.isEmpty();
 * boolean param_8 = obj.isFull();
 */