栈和队列介绍

java中栈的实现方式:

先进后出

Stack<Integer> st = new Stack<Integer>();

队列:

常用LinkedList 集合,它实现了Queue 接口和List接口;

LinkedList底层是一个双向链表

队列主要分为阻塞和非阻塞,有界和无界、单向链表和双向链表之分;

add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
  remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
  element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
  offer 添加一个元素并返回true 如果队列已满,则返回false
  poll 移除并返问队列头部的元素 如果队列为空,则返回null
  peek 返回队列头部的元素 如果队列为空,则返回null
  put 添加一个元素 如果队列满,则阻塞
  take 移除并返回队列头部的元素 如果队列为空,则阻塞
drainTo(list) 一次性取出队列所有元素

栈和队列:

  1. 用队列实现栈

  2. 用栈实现队列

232. 用栈实现队列

用两个栈实现队列,一个作为进栈,一个出栈,调整顺序。

class MyQueue {
    
    Stack<Integer> stackIn = new Stack<Integer>();
    Stack<Integer> stackOut = new Stack<Integer>();

    public MyQueue() {
        stackIn.clear();
        stackOut.clear();
    }

    public void push(int x) {
        stackIn.push(x);
    }

    public int pop() {
        int i = this.peek();
        return stackOut.pop();
    }

    public int peek() {
        if(stackOut.empty()){
            while(!stackIn.empty()){
                stackOut.push(stackIn.pop());
            }
        }
        return stackOut.peek();
    }

    public boolean empty() {
        return stackOut.empty() && stackIn.empty();
    }
}

225. 用队列实现栈

用两个队列实现栈,其中一个保存栈内的元素,另一个作为辅助队列,调证顺序,使新加入的元素永远位于第一个位置。

优化:一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。

class MyStack {
    Queue<Integer> deque1 = new LinkedList<Integer>();
    Queue<Integer> deque2 = new LinkedList<Integer>();
    public MyStack() {
        deque1.clear();
        deque2.clear();
    }

    public void push(int x) {
        deque2.add(x);
        while(!deque1.isEmpty()){
            deque2.add(deque1.remove());
        }
        while (!deque2.isEmpty()){
            deque1.add(deque2.remove());
        }
    }

    public int pop() {
        return deque1.remove();
    }

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

    public boolean empty() {
        return deque1.isEmpty() && deque2.isEmpty();
    }
}

匹配问题:

20.有效的括号

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

  1. 逆波兰表达式求值

涉及到之前遍历的元素,以及匹配问题可以考虑栈解决

20.有效的括号

  1. 左右括号数量相等,顺序相应()[]{}
  2. 左右括号数量不相等}或者{
  3. 左右括号数量相等,顺序不相应(]
  4. 左右括号数量不相等,顺序不相应

分析:用栈解决匹配问题,当前元素如果为左括号栈中添加相应的右括号;如果为右括号,就与栈顶匹配(栈内需不为空),直到最后栈为空就满足条件。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> st = new Stack<>();
        for (int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            if(c == '(') st.push(')');
            else if(c == '[') st.push(']');
            else if(c == '{') st.push('}');
            else if(!st.empty() && c == st.peek()){
                st.pop();
            }else{
                return false;
            }
        }
        if(st.empty())  return true;
        else return false;
    }
}

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

方法1: 栈(stack或者直接使用Stringbuffer作为栈)

class Solution {
    public String removeDuplicates(String s) {
        Stack<Character> st = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(st.empty()){
                st.push(c);
            }else{
                if(st.peek() == c){
                    st.pop();
                }else{
                    st.push(c);
                }
            }
        }
        StringBuffer list = new StringBuffer();
        while(!st.empty()){
            list.insert(0, st.pop());
        }
        return new String(list);
    }
}

方法3: 由于本题是删除字符,可以用快慢指针

class Solution {
    public String removeDuplicates(String s) {
        int slow = 0, fast = 0;
        char[] chars = s.toCharArray();
        while (fast < s.length()){
            chars[slow] = chars[fast];
            if(slow > 0 && chars[slow] == chars[slow-1]){
                slow--;
            }else{
                slow++;
            }

            fast++;
        }
        return String.valueOf(chars, 0, slow);
    }
}
  1. 逆波兰表达式求值

注意,减法和除法计算与弹栈的顺序问题

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> st = new Stack<>();
         for (String t: tokens){
             if(t.equals("+")){
                 int a = st.pop();
                 int b = st.pop();
                 st.push(a+b);
             }else if(t.equals("-")){
                 int a = st.pop();
                 int b = st.pop();
                 st.push(b-a);	//注意减数与被减数
             }else if(t.equals("*")){
                 int a = st.pop();
                 int b = st.pop();
                 st.push(a*b);
             }else if(t.equals("/")){
                 int a = st.pop();
                 int b = st.pop();
                 st.push(b/a);	//注意除数与被除数

             }else{
                 int i = Integer.parseInt(t);
                 st.push(i);
             }
         }
         return st.pop();
    }
}

单调队列

一、单调队列的概念:

单调队列,即单调递减或单调递增的队列。

为了可以同时弹出队首和队尾的元素,一般需要使用双端队列。满足这种单调性的双端队列称作「单调队列」。

二、单调队列的性质:
  1. 队列中的元素在原来的列表中的位置是由前往后的(随着循环顺序入队)。

  2. 队列中元素的大小是单调递增或递减的。

三、单调队列的特点:

从队尾入列,队首或队尾出列。

四、单调队列的作用

单调队列一般用于求区间内的最值问题。

五、例题
  1. 滑动窗口最大值

239. 滑动窗口最大值


class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int [] res = new int[nums.length-k+1];
        //que 保存的数组下标
        LinkedList<Integer> que = new LinkedList<>();
        for (int i = 0; i < nums.length; i++) {
            //保证队列单调递减,队首即为当前窗口最大值
            while(!que.isEmpty() && nums[i] > nums[que.peekLast()]){
                que.removeLast();
            }
            que.add(i);
            //窗口左边界left,右边界i
            int left = i - k + 1;
            //如果队首下标小于左边界,表示窗口已经划过最大值,必须被删除
            if(que.peek() < left){
                que.removeFirst();
            }
            //当窗口内元素大于等于k时,开始保存
            if(i + 1 >= k){
                res[left] = nums[que.peek()];
            }

        }
        return res;
    }
}

官网版:更好理解一点

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int [] res = new int[nums.length-k+1];
        //que 保存的数组下标
        LinkedList<Integer> que = new LinkedList<>();
        //先添加第一个窗口
        for (int i = 0; i < k; i++) {
            //保证队列单调递减,队首即为当前窗口最大值
            while(!que.isEmpty() && nums[que.peekLast()] < nums[i]){
                que.removeLast();
            }
            que.add(i);
        }
        //第一个最大值
        res[0] = nums[que.peek()];
        //然后再移动
        for (int i = k; i < nums.length; i++) {
            //保证队列单调递减,队首即为当前窗口最大值
            while(!que.isEmpty() && nums[que.peekLast()] < nums[i]){
                que.removeLast();
            }
            que.add(i);
            int left = i - k;
            if(que.peek() <= left){
                que.remove();
            }
            res[i-k+1] = nums[que.peek()];
        }
        return res;
    }
}

优先队列

优先级队列的内部是用堆来维护,而且优先级队列内部元素是自动依照元素的权值排列。

缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,内部以二叉树的形式。

java中用PriorityQueue集合类实现

  1. 前 K 个高频元素

  2. 数组中的第K个最大元素

使用最小堆统计前k个最大的元素,使用最大堆统计前k个最小的元素

347. 前 K 个高频元素

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //统计元素出现次数
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i: nums){
            map.put(i, map.getOrDefault(i,0)+1);
        }
        //构建最小堆,保存元素的值
        PriorityQueue<Integer> que = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return map.get(o1) - map.get(o2);
            }
        });
        //当出现次数大于最小值时就弹出最小值,一直维持大小为k的堆,就是前最大k的值
        for(Map.Entry<Integer, Integer> entry:map.entrySet()){
            if(que.size() < k){
                que.add(entry.getKey());
            }else if(entry.getValue() > map.get(que.peek())){
                que.remove();
                que.add(entry.getKey());
            }
        }
        //输出
        int [] res = new int[k];
        int i = 0;
        while(!que.isEmpty()){
            res[i++] = que.remove();
        }
        return res;
    }
}
  1. 数组中的第K个最大元素
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //构建最小堆
        PriorityQueue<Integer> que = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        //当出现次数大于最小值时就弹出最小值,一直维持大小为k的堆,就是前最大k的值
        for(int i: nums){
            if(que.size() < k){
                que.add(i);
            }else if(que.peek() < i){
                que.remove();
                que.add(i);
            }
        }
        
        return que.peek();
    }
}

你可能感兴趣的:(Leetcode,leetcode,java)