stack queue Leetcode 栈和队列算法题

232.用栈实现队列

Queue 是 Collection 接口下的,她的一个实现类是ArrayDeque.

不推荐使用 Vector 实现的 Stack,因为为了保证线程安全使得 Stack 的效率很低,而且由于继承的 Vector 导致没有屏蔽一些栈不应该有的操作

  • stack 下使用入栈出栈的Api是 push 和 pop

两个栈实现队列:

  • 一个栈用于放数据
  • 一个栈用于读数据,这时读出的数据是按照队列的顺序的,
  • 所以每次读的时候就先判断读数据的栈是否为空,先从读数据的栈中拿数据,如果为空,则把存数据的栈倒入读数据的栈
  • 每次读完不需要再把数据倒回去,因为是优先从读数据的栈中取数据的,后加入的数据不会和之前的数据混乱,因为之前的数据读完了才能轮到后面的数据
java 复制代码
class MyQueue {

    private Deque<Integer> inStack;
    private Deque<Integer> outStack;

    public MyQueue() {
        inStack = new ArrayDeque<Integer>();
        outStack = new ArrayDeque<Integer>();
    }
    
    public void push(int x) {
        inStack.push(x);
    }

    private void inToOut(){
        while(!inStack.isEmpty()){
            outStack.push(inStack.pop());
        }
    }
    
    public int pop() {
        if(outStack.isEmpty()){
            inToOut();
        }
        return outStack.pop();
    }
    
    public int peek() {
        if(outStack.isEmpty()){
            inToOut();
        }
        return outStack.peek();
    }
    
    public boolean empty() {
        if(outStack.isEmpty() && inStack.isEmpty()){
            return true;
        }
        return false;
    }
}

225.用队列实现栈

用两个队列实现栈,第一个队列用于按照栈的顺序存放队列,当来一个新数据 x 如需要入栈的时候,我们要把这个数据 x 放在队列的最前面

队列的实现方式:LinkedList 实现了 Deque 和 List 两个接口

Deque是双端队列,既可以当作栈,也可以当作队列,当作队列使用时:

  • 入队尾用 offer() / add()
  • 出队头用 poll()
  • 与 add() push() pop() 的不同在与
    • offer/poll不会抛出异常
    • push() 是针对栈的操作,如果在队列中使用的话,添加的元素是加载队头的
java 复制代码
Deque<Integer> queue = new LinkedList<>();

实现方法一:

  • queue1 把所有的数据先倒入 queue2,然后把 x 放入 queue1 里
  • 然后再把 queue2 里的数据倒回 queue1,这样新加入的数据就在 queue1 队列头部
  • 问题是这样原本的数据在两个队列中倒了两次(移动了两次)
java 复制代码
class MyStack {
    private Deque<Integer> queue1;
    private Deque<Integer> queue2;

    public MyStack() {
        queue1 = new LinkedList<Integer>();
        queue2 = new LinkedList<Integer>();
    }

    public void push(int x) {
        while(!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        queue1.offer(x);
        while(!queue2.isEmpty()){
            queue1.offer(queue2.poll());
        }
    }

    public int pop() {
        return queue1.poll();
    }

    public int top() {
        return queue1.peek();
    }

    public boolean empty() {
        return queue1.isEmpty();
    }
}

实现方法二:

  • x 放入 queue2 中,这样新加入的数据就在 queue2 队列头部
  • 然后在把 queue1 中的数据导入 queue2 中,这样 queue2 的数据就是按照栈的顺序存放的
  • 最后交换 queue1 和 queue2 指向的队列,这样 queue1就指向的是那个按照栈顺序存放的队列,而 queue2变成了空队列
  • 这样数据只倒了一次
java 复制代码
class MyStack {
    private Deque<Integer> queue1;
    private Deque<Integer> queue2;

    public MyStack() {
        queue1 = new LinkedList<Integer>();
        queue2 = new LinkedList<Integer>();
    }

    public void push(int x) {
        queue2.offer(x);
        while(!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        Deque<Integer> tempQ = queue1;
        queue1 = queue2;
        queue2 = tempQ;
    }

    public int pop() {
        return queue1.poll();
    }

    public int top() {
        return queue1.peek();
    }

    public boolean empty() {
        return queue1.isEmpty();
    }
}

20.有效的括号

题目的描述:

交错的括号不能出现,但是包含的括号可以出现,例如:[(]) 是 false,但是 [()] 是 true

解法:

  • 当遇到左括号的时候入栈
  • 当遇到右括号的时候,取出栈顶元素,判断能否与右括号匹配

注意:

  • 简化代码的方法,遇到左括号,存对应的右括号
  • 到时候遇到右括号,需要判断栈顶和右括号是否匹配的时候,直接判断是否相等就行,而不用三个分别判断
java 复制代码
class Solution {
    public boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for(int i = 0; i < s.length(); ++ i){
            char ch = s.charAt(i);
            if(ch == '(') stack.push(')');
            else if(ch == '{') stack.push('}');
            else if(ch == '[') stack.push(']');
            else if(stack.isEmpty() || stack.pop() != ch){
                return false;
            }
        }
        if(!stack.isEmpty()){
            return false;
        }
        return true;
    }
}

我的代码

java 复制代码
class Solution {
    public boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for(int i = 0; i < s.length(); ++ i){
            char ch = s.charAt(i);
            if(ch == ')' || ch == '}' || ch == ']'){
                if(stack.isEmpty()){
                    return false;
                }
                char top = stack.peek();
                if((top == '(' && ch == ')') 
                || (top == '{' && ch == '}')
                || (top == '[' && ch == ']')){
                    stack.pop();
                }else{
                    return false;
                }
            }else{
                stack.push(ch);
            }
        }
        if(!stack.isEmpty()){
            return false;
        }
        return true;
    }
}

1047.删除字符串中的所有相邻重复项

括号匹配的另一种描述形式

可以用StringBuilder简化代码,字符串本身提供删除指定索引位置的API:

java 复制代码
class Solution {
    public String removeDuplicates(String s) {
        StringBuilder sb = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (sb.length() != 0 && sb.charAt(sb.length() - 1) == c) {
                sb.deleteCharAt(sb.length() - 1);
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
java 复制代码
class Solution {
    public String removeDuplicates(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for(int i = 0; i < s.length(); ++ i){
            char ch = s.charAt(i);
            if(stack.isEmpty() || stack.peek() != ch){
                stack.push(ch);
            }else{
                stack.pop();
            }
        }
        char[] ans = new char[stack.size()];
        int idx = stack.size() - 1;
        while(!stack.isEmpty()){
            ans[idx--] = stack.pop();
        }
        return new String(ans);
    }
}

150.逆波兰表达式求值

  • 遇到运算符则从栈顶取两个数字出来,第一个数字是运算符右边的,第二个才是左边的,然后根据运算符的类型,把计算结果存回栈中,便于后续的计算
  • 如果遇到数字,就存入栈中,等待后续被取出
java 复制代码
class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new ArrayDeque<>();
        for(int i = 0; i < tokens.length; ++ i){
            String token = tokens[i];
            if(token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/")){
                int r = stack.pop();
                int l = stack.pop();
                if(token.equals("+")) stack.push(l+r);
                else if(token.equals("-")) stack.push(l-r);
                else if(token.equals("*")) stack.push(l*r);
                else stack.push(l/r);
            }else{
                stack.push(Integer.parseInt(token));
            }
        }
        return stack.pop();
    }
}

239.滑动窗口最大值

动态维护一个保持下标序列的递减队列

  • 窗口要退出一个元素:
    • 这个元素可能早已出队
    • 也可能正是当前队列的最大值(队首),如果是队首就需要出队了
  • 当新加入一个元素时,
    • 如果这个元素比队尾的值大,则说明这个元素的"价值"更高。
    • 因为这个元素不仅更偏右边,而且更大,那么之前那些存在队列中比这个元素小的值就都可以从队尾出队了

注意:

  • poll 是从队首出队
  • pollLast 从队尾出队
  • offer 是从队尾入队,等同于 offerLast
java 复制代码
class Solution {
  public int[] maxSlidingWindow(int[] nums, int k) {
    Deque<Integer> queue = new LinkedList<>();
    int[] ans = new int[nums.length - k + 1];
    int idx = 0;
    for(int i = 0; i < k; ++ i){
      while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
        queue.pollLast();
      }
      queue.offerLast(i);
    }

    ans[idx++] = nums[queue.peekFirst()];
    for(int i = k; i < nums.length; ++ i){
      if(queue.peekFirst() == i - k) queue.poll();
      while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
        queue.pollLast();
      }
      queue.offerLast(i);
      ans[idx++] = nums[queue.peekFirst()];
    }
    return ans;
  }
}

347.前 K 个高频元素

求前 K 个最大 的元素:

  • 用优先队列维护一个size为k的最小堆,这样最小的元素保持在堆顶
  • 当有新元素来的时候
    • 如果新元素大于堆顶的最小元素,说明这个堆顶的元素要被淘汰掉,新元素加入堆中
    • 如果新元素小于堆顶元素,说明新元素连最小的元素都打不过,自然堆中的元素是当前最大的K个,堆保持不变
java 复制代码
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> table = new HashMap<>();
        for(int i = 0; i < nums.length; ++ i){
            table.put(nums[i], table.getOrDefault(nums[i], 0) + 1);
        }
        int[] ans = new int[k];
        PriorityQueue<Integer> pq = new PriorityQueue<>((Integer a, Integer b)->{
            return table.get(a) - table.get(b); 
        });

        for(int key : table.keySet()){
            if(pq.size() < k){
                pq.add(key);
            }else if(table.get(pq.peek()) < table.get(key)){
                pq.poll();
                pq.add(key);
            }
        }
        int idx = 0;
        while(!pq.isEmpty()){
            ans[idx++] = pq.poll();
        }
        return ans;
    }
}
相关推荐
吾日三省吾码2 小时前
JVM 性能调优
java
LNTON羚通2 小时前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
弗拉唐3 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi773 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3434 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀4 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
哭泣的眼泪4084 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
清炒孔心菜4 小时前
每日一题 LCR 078. 合并 K 个升序链表
leetcode
蓝黑20204 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea