算法刷题二:栈和队列,Java中栈和队列中相关的API,LeetCode中常见的栈和队列的题目详解

Java中的栈和队列

Java中的栈

Stack

在这里插入图片描述
empty():检查
push():往栈中放入元素
peek():查看栈顶元素
pop():弹出栈顶元素
search():搜索栈中的元素,并返回下标。下标从1开始,栈顶下标为1。

使用Deque来作为栈

Stack这个类官方都已经不推荐使用了,所以可以使用Deque来作为栈。需要注意的是,Deque是一个接口,但是Stack并不是接口,而是一个类。

Deque的详情可以看下面队列中的介绍,如下是Deque作为Stack时使用的方法。
在这里插入图片描述

Java中的队列

Queue

注意看图:Deque属于Queue的子接口。
在这里插入图片描述
Queue中的方法总结
Insert类方法:插入元素到队尾
Remove类方法:移除队头元素
Examine类方法:查看对头元素
在这里插入图片描述

PriorityQueue

优先队列,使用得比较多的一种队列。
优先队列的实现方法主要有两种:(Heap)和二叉搜索树(Binary Search Tree)

堆的实现方式也有很多种,如下表所示:Java中的PriorityQueue是使用严格斐波那契堆实现的,且默认是小顶堆。
在这里插入图片描述
二叉搜索树本质上就是二叉树,只不过多了一些约束,得保证任意一个节点,它的左子树的节点都要小于它本身,它的右子树的节点都要大于它本身。
如下所示便是一颗二叉搜索树,不过其构造过于特殊,也是一棵二叉平衡树,不仅要满足二叉搜索树的约束,还得保证左右子树的高度不能相差太大。

  • 这是一颗二叉搜索树也是一颗二叉平衡树。
    在这里插入图片描述
  • 这是一颗二叉搜索树,但不是一颗二叉平衡树。
    在这里插入图片描述

Deque

在这里插入图片描述
Deque作为Stack时,可以使用如下方法:
在这里插入图片描述

Deque作为Queue时,可以使用如下方法
在这里插入图片描述
Deque的常用方法示意图
在这里插入图片描述

本篇文章涉及到的题目

LeetCode232.用栈实现队列

LeetCode155.最小栈

LeetCode20. 有效的括号

LeetCode225.用队列实现栈

LeetCode703. 数据流中的第 K 大元素

LeetCode239. 滑动窗口最大值

剑指 Offer 59 - II. 队列的最大值

题目练习(包含代码)

栈相关的题目

LeetCode232. 用栈实现队列

题目链接:LeetCode232. 用栈实现队列

题目描述

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x)将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek()返回队列开头的元素
boolean empty()如果队列为空,返回 true ;否则,返回 false
说明:
你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

我使用了Java中的Deque来实现队列。用两个栈来模拟一个队列。

  • Deque中的addFirst方法等效于Stack中的push方法
  • Deque中的removeFirst方法等效于Stack中的pop方法
  • Deque中的getFirst方法等效于Stack中的peek方法

我用两个栈来实现一个队列。
push的时候,将push的元素放入inputStack中。一旦要pop或者是peek,则将inputStack中的元素全部出栈依次放入outputStack中。然后从outputStack中取出栈顶元素,即相当于取出队列的队头元素。

class MyQueue {

 Deque<Integer> inputStack;
 Deque<Integer> outputStack;

 /**
  * Initialize your data structure here.
  */
 public MyQueue() {
   inputStack = new LinkedList<>();
   outputStack = new LinkedList<>();
 }

 /**
  * Push element x to the back of queue.
  */
 public void push(int x) {
   inputStack.addFirst(x);
 }

 /**
  * Removes the element from in front of queue and returns that element.
  */
 public int pop() {
   if (!outputStack.isEmpty()) {
     return outputStack.removeFirst();
   }
   while (!inputStack.isEmpty()) {
     outputStack.addFirst(inputStack.removeFirst());
   }
   if (!outputStack.isEmpty()) {
     return outputStack.removeFirst();
   } else {
     return -1;
   }
 }

 /**
  * Get the front element.
  */
 public int peek() {
   if (!outputStack.isEmpty()) {
     return outputStack.getFirst();
   }
   while (!inputStack.isEmpty()) {
     outputStack.addFirst(inputStack.removeFirst());
   }
   if (!outputStack.isEmpty()) {
     return outputStack.getFirst();
   } else {
     return -1;
   }
 }

 /**
  * Returns whether the queue is empty.
  */
 public boolean empty() {
   return (inputStack.isEmpty() && outputStack.isEmpty()) ? true : false;
 }
}

LeetCode155.最小栈

题目链接:LeetCode155.最小栈

题目概要

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top()—— 获取栈顶元素。
getMin()—— 检索栈中的最小元素。

需要注意的是获取最小元素的过程是一个动态的过程,当我们往栈中放元素时,可以用一个变量记录最小值。但是当我们从栈顶pop一个元素时,此时栈中元素最小值是多少?又pop了一个元素,此次栈中元素的最小值是多少?然后又push了一个元素,此次栈中元素的最小值又是多少?在我们每次放栈中push元素或者是pop元素时,我们都要重新计算最小值。

方法一:使用List + Sort进行排序

采用这种解法时间复杂度很高,每次push一个元素都要对栈中的元素进行排序,获得最小值。

class MinStack {
  private List<Integer> list;

  public MinStack() {
    this.list = new ArrayList<>();
  }

  public void push(int x) {
    this.list.add(x);
  }

  public void pop() {
    if (this.list.size() == 0) {
      return;
    } else {
      this.list.remove(this.list.size() - 1);
    }
  }

  public int top() {
    if (this.list.isEmpty()) {
      return Integer.MIN_VALUE;
    } else {
      return this.list.get(this.list.size() - 1);
    }
  }

  public int getMin() {
    if (this.list.isEmpty()) {
      return Integer.MIN_VALUE;
    }
    Object[] objects = list.toArray();
    Arrays.sort(objects);
    return (Integer) objects[0];
  }
}
方法二:使用一个辅助栈

这种方法效率很高,可以O(1)获取栈中的最小值。使用一个辅助栈—minStack,用来存储栈中的最大值。

  • 每次push一个元素进栈时,首先从minStackpeek栈顶元素,如果发现当前的值比minStack中的栈顶元素大时,将该元素放入stack的时候同时也将其放入minStack中。如果当前要放入的元素比minStack的栈顶元素小时,则读出(注意是peek不是pop)minStack的栈顶元素,再将其放入minStack
  • 每次获取栈中的最大元素时,只需要返回minStack的栈顶元素就可以。
class MinStack1 {
  //存储数据
  Deque<Integer> stack;

  //存储截止到当前元素,栈中的最小元素
  Deque<Integer> minStack;

  public MinStack1() {
    stack = new LinkedList<>();
    minStack = new LinkedList<>();
  }

  public void push(int x) {
    //要个栈要么都是空的,要么都有元素
    if (stack.isEmpty() && minStack.isEmpty()) {
      minStack.offerFirst(x);
    } else {
      minStack.offerFirst(Math.min(stack.peekFirst(), x));
    }
    stack.offerFirst(x);
  }

  public void pop() {
    if (!stack.isEmpty() && !minStack.isEmpty()) {
      stack.pollFirst();
      minStack.pollFirst();
    }
  }

  public int top() {
    return stack.isEmpty() ? -1 : stack.peekFirst();
  }

  public int getMin() {
    return minStack.isEmpty() ? -1 : minStack.peekFirst();
  }
}

LeetCode20. 有效的括号

题目链接:LeetCode20. 有效的括号

题目概要

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

使用栈来解决这个问题。

public boolean isValid(String s) {
  if (s == null || s.length() == 0) {
    return false;
  }
  Deque<String> stack = new LinkedList<>();
  String leftBrackets = "({[";
  Map<String, String> matching = new HashMap<>();
  matching.put(")", "(");
  matching.put("]", "[");
  matching.put("}", "{");
  for (int i = 0; i < s.length(); ++i) {
    String bracket = s.substring(i, i + 1);
    //遇到左括号直接将其入栈
    if (leftBrackets.contains(bracket)) {
      stack.addFirst(bracket);
    } else {
      //当遍历字符串时遇到右括号,栈顶元素出栈,看是否与该右括号配对
      if (stack.isEmpty()) {
        return false;
      } else if (!matching.get(bracket).equals(stack.removeFirst())) {
        return false;
      }
    }
  }
  if (stack.isEmpty()) {
    return true;
  } else {
    return false;
  }
}

队列相关的题目

LeetCode225.用队列实现栈

题目链接:LeetCode225.用队列实现栈

题目概要

使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
注意:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

我用两个队列来实现一个栈。
当发生push操作时。检查bufferQueue1bufferQueue2,如一个队列为空,另一个不为空,则将元素放入不为空的队列中。如果都为空,则默认放入第一个队列(第一次push操作时,两个队列都为空)
当发生pop或者是top操作时,则将一个队列的元素全部出队(仅保留最后一个元素)放入另一个队列,这时,剩下的这个元素便是出栈的栈顶元素。poptop的操作及其类似,唯一的不同是:一个得到了栈顶元素,然后将其删掉;而另一个得到了了栈顶元素,不用将其删掉。(注意:队列的特点是先入先出)

class MyStack {

  private Queue<Integer> bufferQueue1;
  private Queue<Integer> bufferQueue2;


  /**
   * Initialize your data structure here.
   */
  public MyStack() {
    bufferQueue1 = new LinkedList<>();
    bufferQueue2 = new LinkedList<>();
  }

  /**
   * Push element x onto stack.
   */
  public void push(int x) {
    //有两个队列,如果两个队列都为空,则往bufferQueue1添加,
    // 如果一个为空,一个不为空,则往不为空的队列中添加元素
    if ((bufferQueue1.isEmpty()) && (!bufferQueue2.isEmpty())) {
      bufferQueue2.offer(x);
    } else if ((!bufferQueue1.isEmpty()) && (bufferQueue2.isEmpty())) {
      bufferQueue1.offer(x);
    } else if ((bufferQueue1.isEmpty()) && (bufferQueue2.isEmpty())) {
      bufferQueue1.offer(x);
    }
  }

  /**
   * Removes the element on top of the stack and returns that element.
   */
  public int pop() {
    if (this.empty() == true) {
      return -1;
    }
    //bufferQueue1不为空,bufferQueue2不为空
    if (!bufferQueue1.isEmpty() && bufferQueue2.isEmpty()) {
      while (bufferQueue1.size() != 1) {
        bufferQueue2.offer(bufferQueue1.poll());
      }
      return bufferQueue1.poll();
    }
    //bufferQueue2不为空,bufferQueue1为空
    if (bufferQueue1.isEmpty() && !bufferQueue2.isEmpty()) {
      while (bufferQueue2.size() != 1) {
        bufferQueue1.offer(bufferQueue2.poll());
      }
      return bufferQueue2.poll();
    }
    return -1;
  }

  /**
   * Get the top element.
   */
  public int top() {
    if (this.empty() == true) {
      return -1;
    }
    //bufferQueue1不为空,bufferQueue2不为空
    if (!bufferQueue1.isEmpty() && bufferQueue2.isEmpty()) {
      while (bufferQueue1.size() != 1) {
        bufferQueue2.offer(bufferQueue1.poll());
      }
      int tmp = bufferQueue1.poll();
      bufferQueue2.offer(tmp);
      return tmp;
    }
    if (bufferQueue1.isEmpty() && !bufferQueue2.isEmpty()) {
      while (bufferQueue2.size() != 1) {
        bufferQueue1.offer(bufferQueue2.poll());
      }
      int tmp = bufferQueue2.poll();
      bufferQueue1.offer(tmp);
      return tmp;
    }
    return -1;
  }

  /**
   * Returns whether the stack is empty.
   */
  public boolean empty() {
    return ((bufferQueue1.isEmpty()) && (bufferQueue2.isEmpty())) ? true : false;
  }
}

LeetCode703.数据流中的第 K 大元素

题目链接:LeetCode703. 数据流中的第 K 大元素

题目描述

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 返回当前数据流中第 k 大的元素。
示例:
输入:
[“KthLargest”, “add”, “add”, “add”, “add”, “add”]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]
解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8

示意图
在这里插入图片描述

LeetCode239. 滑动窗口最大值

题目链接:LeetCode239. 滑动窗口最大值

题目概要

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

方法一:使用排序找每个窗口的最大值(未通过)

这是拿到这个题目最朴素的想法。在每个窗口中都进行排序,然后取该窗口中的元素排序以后的最后一个元素,由于是从小到达进行排序的,最后一个元素就是该窗口的最大值。但是遇到数据量大的情况,就不行了,最终因为超时没有通过。

问题出在每个窗口都要排序,有很多重复劳作,太费时间了。

public int[] maxSlidingWindow(int[] nums, int k) {
  if (nums == null || nums.length == 0) {
    return null;
  }
  //当数组长度小于窗口长度时,直接返回数组中的最大值
  if (nums.length < k) {
    Arrays.sort(nums);
    return new int[]{nums[nums.length - 1]};
  }
  int start = 0, numsLength = nums.length, index = 0;
  int[] array = new int[numsLength - k + 1];
  while ((start + k - 1) < numsLength) {
    int end = start + k - 1;
    int max = findSlindingWindowMaxValue(nums, start, end);
    array[index++] = max;
    start++;
  }
  return array;
}

private int findSlindingWindowMaxValue(int[] nums, int start, int end) {
  int tmpLength = end - start + 1;
  int[] tmpArray = new int[tmpLength];
  int j = 0;
  for (int i = start; i <= end; ++i) {
    tmpArray[j++] = nums[i];
  }
  //从小到大进行排序,最大值则是数组最后一个元素
  //Arrays.sort使用的是快排,平均时间复杂度为o(NlogN)
  Arrays.sort(tmpArray);
  return tmpArray[tmpLength - 1];
}

在这里插入图片描述
来看看这个数据量:

图上只是一小部分。
在这里插入图片描述

方法二:使用双端队列Deque

版本一:Deque中存储滑动窗口中的值

/**
  * Deque中存储的是数字
  * @param nums
  * @param k
  * @return
  */
 public int[] maxSlidingWindow(int[] nums, int k) {
   if (nums == null || nums.length == 0) {
     return null;
   }
   Deque<Integer> deque = new LinkedList<>();
   int[] array = new int[nums.length - k + 1];
   int index = 0;
   for (int i = 0; i < nums.length; ++i) {
     //不能只通过deque的size大小来判断是否需要remove队头的元素
     //这样的做法欠妥,应该是存入数组的下标,而不是存入数组的值
     //TODO 感觉用nums[i - k] == deque.peekFirst()来判断窗口中的元素是否大于窗口的容量有点勉强
     if (deque.size() >= k || ((i >= k) && (nums[i - k] == deque.peekFirst()))) {
       deque.removeFirst();
     }
     while (!deque.isEmpty() && nums[i] > deque.peekLast()) {
       deque.pollLast();
     }
     deque.addLast(nums[i]);
     if (i >= k - 1) {
       array[index++] = deque.peekFirst();
     }
   }
   return (nums.length < k) ? new int[]{deque.peekFirst()} : array;
 }

版本二:Deque中存储窗口中值的下标
个人更推荐这种写法,同时也为以后做这样的题提供了一个很好的思路:不要总是想着存储值,我们可以存储该值所对应的数组的下标,最后通过下标来获取值。这样的好处是,下标是唯一的,能唯一的区分每个元素。

public int[] maxSlidingWindow(int[] nums, int k) {
   if (nums == null || nums.length == 0) {
     return null;
   }
   Deque<Integer> deque = new LinkedList<>();
   int[] array = new int[nums.length - k + 1];
   int index = 0;
   for (int i = 0; i < nums.length; ++i) {
     //当deque为空时,deque.peekFirst()返回null
     if (!deque.isEmpty() && ((i - deque.peekFirst()) >= k)) {
       deque.removeFirst();
     }
     while (!deque.isEmpty() && (nums[i] > nums[deque.peekLast()])) {
       deque.pollLast();
     }
     //offerLast:插入成功返回true,插入失败可能会返回false(因容量问题插入失败会返回false,其它问题也会抛出异常)
     //addLast:插入失败直接抛出异常
     deque.offerLast(i);
     if (i >= k - 1) {
       array[index++] = nums[deque.peekFirst()];
     }
   }
   return (nums.length < k) ? new int[]{deque.peekFirst()} : array;
 }
方法三:结合剑指 Offer 59 - II. 队列的最大值中的最大队列

剑指 Offer 59 - II. 队列的最大值中写的那个能获取队列中最大值的MaxQueue拿过来。每次从队头出一个元素,然后从队尾进一个元素,然后O(1)获得队列中的最大值,放入结果集中。

class Solution {

    MaxQueue queue = new MaxQueue();

    public int[] maxSlidingWindow(int[] nums, int k) {
      if (nums == null || nums.length == 0) {
        return null;
      }
      int[] ans = new int[nums.length - k + 1];
      for (int i = 0; i < k; ++i) {
        queue.push_back(nums[i]);
      }
      ans[0] = queue.max_value();
      int index = 1;
      for (int j = k; j < nums.length; ++j) {
        queue.pop_front();
        queue.push_back(nums[j]);
        ans[index++] = queue.max_value();
      }
      return ans;
    }

    /**
     * 优化版
     */
    class MaxQueue {
      private Queue<Integer> queue;
      private Deque<Integer> maxQueue;

      public MaxQueue() {
        queue = new LinkedList<Integer>();
        maxQueue = new LinkedList<Integer>();
      }

      public int max_value() {
        if (maxQueue.isEmpty()) {
          return -1;
        }
        return maxQueue.peekFirst();
      }

      public void push_back(int value) {
        while (!maxQueue.isEmpty() && maxQueue.peekLast() < value) {
          maxQueue.pollLast();
        }
        maxQueue.offerLast(value);
        queue.offer(value);
      }

      public int pop_front() {
        if (queue.isEmpty()) {
          return -1;
        }
        int ans = queue.poll();
        if (ans == maxQueue.peekFirst()) {
          maxQueue.pollFirst();
        }
        return ans;
      }
    }
  }

能通过题解,但是比第二种方法的性能差一些。
在这里插入图片描述

剑指 Offer 59 - II. 队列的最大值

题目链接:剑指 Offer 59 - II. 队列的最大值

题目概要

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1

思路与LeetCode225.用队列实现栈有些相似。我总共用了三个队列。其中用了两个双端队列来实现一个maxQueue,用来存储当前队列中的最大值。然后再使用一个队列用来存储放入队列中的值。

当往队列中添加一个元素时,对应的maxQueue中也要放入相应的元素,maxQueue有两个,一个总是保持空的状态,一个用来存储最大值。每次往队列中放入元素时,也需要往maxQueue中添加当前队列的最大值,不管是从maxQueue中读取最大值,还是将元素放入maxQueue,都总是操作不为空的那个maxQueue
每次往队列中放入元素,先从maxQueuepeek出队尾(peekLast)的元素。

  • 如果要放入队列中的元素大于队尾的元素,则会发生迁移操作。将该maxQueue中的元素取出(pollLast),依次放入另一个maxQueue,如果取出的元素要放入队列中的元素大,则将取出的元素直接放入(offerFirst)另一个maxQueue。如果比要放入队列中的元素小,则在对应位值放入(offerFirst)要放入队列中的元素

  • 如果要放入队列中的元素小于队尾的元素则直接放入(offerLast)不为空的那个maxQueue中。

文字叙述有点繁杂,可以看图
整个示意图如下:
在这里插入图片描述
代码

/**
 * 定义一个队列并实现函数 max_value 得到队列里的最大值
 */
public class MaxQueue {
  Queue<Integer> queue;
  Deque<Integer> maxQueue1;
  Deque<Integer> maxQueue2;


  public MaxQueue() {
    queue = new LinkedList<>();
    maxQueue1 = new LinkedList<>();
    maxQueue2 = new LinkedList<>();
  }

  public int max_value() {
    return getValueFromMaxQueue();
  }

  public void push_back(int value) {
    queue.offer(value);
    if (maxQueue1.isEmpty() && maxQueue2.isEmpty()) {
      maxQueue1.offerLast(value);
    } else if (!maxQueue1.isEmpty() && maxQueue2.isEmpty()) {
      pushValueInMaxQueue(value, maxQueue1, maxQueue2);
    } else if (maxQueue1.isEmpty() && maxQueue1.isEmpty()) {
      pushValueInMaxQueue(value, maxQueue2, maxQueue1);
    }
  }

  /**
   * @param value
   * @param queue1 queu1不为空
   * @param queue2 queue2为空
   */
  public void pushValueInMaxQueue(int value, Deque<Integer> queue1, Deque<Integer> queue2) {
    if (value <= queue1.peekLast()) {
      queue1.offerLast(value);
    } else {
      while (!queue1.isEmpty()) {
        int tmp = queue1.pollLast();
        if (tmp <= value) {
          queue2.addFirst(value);
        } else {
          queue2.addFirst(tmp);
        }
      }
      queue2.addLast(value);
    }
  }

  public void deleteValueFromMaxQueue() {
    if (!maxQueue1.isEmpty() && maxQueue2.isEmpty()) {
      maxQueue1.pollFirst();
    } else if (maxQueue1.isEmpty() && !maxQueue2.isEmpty()) {
      maxQueue2.pollFirst();
    }
  }

  public int getValueFromMaxQueue() {
    if (maxQueue1.isEmpty() && maxQueue2.isEmpty()) {
      return -1;
    } else if (!maxQueue1.isEmpty() && maxQueue2.isEmpty()) {
      return maxQueue1.peekFirst();
    } else if (maxQueue1.isEmpty() && !maxQueue2.isEmpty()) {
      return maxQueue2.peekFirst();
    }
    return -1;
  }

  public int pop_front() {
    if (!queue.isEmpty()) {
      deleteValueFromMaxQueue();
      return queue.poll();
    } else {
      return -1;
    }
  }
}

通过LeetCode倒是没问题,可以耗时太长了,得优化。
在这里插入图片描述
主要耗时地方在于我的程序设计中,是两个队列来实现maxQueue,会发生很多数据的搬移。

看看官方的题解:maxQueue只用了一个队列便实现了,没有发生大量的数据迁移的动作,类似于上文 LeetCode239. 滑动窗口最大值 题解中的方法二。

class MaxQueue {
  Queue<Integer> queue;
  Deque<Integer> maxQueue;

  public MaxQueue() {
    queue = new LinkedList<Integer>();
    maxQueue = new LinkedList<Integer>();
  }

  public int max_value() {
    if (maxQueue.isEmpty()) {
      return -1;
    }
    return maxQueue.peekFirst();
  }

  public void push_back(int value) {
    while (!maxQueue.isEmpty() && maxQueue.peekLast() < value) {
      maxQueue.pollLast();
    }
    maxQueue.offerLast(value);
    queue.offer(value);
  }

  public int pop_front() {
    if (queue.isEmpty()) {
      return -1;
    }
    int ans = queue.poll();
    if (ans == maxQueue.peekFirst()) {
      maxQueue.pollFirst();
    }
    return ans;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值