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;
    }
}
相关推荐
好奇龙猫6 分钟前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
P.H. Infinity33 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天37 分钟前
java的threadlocal为何内存泄漏
java
sp_fyf_202442 分钟前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
caridle1 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^1 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋31 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
香菜大丸1 小时前
链表的归并排序
数据结构·算法·链表
jrrz08281 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表