leetcode--栈与队列

本文介绍了如何使用栈和队列的数据结构来实现彼此的功能,如用两个栈实现队列,用队列实现栈,并展示了这些结构在解决实际问题中的应用,如有效的括号检查、删除字符串中的重复项、逆波兰表达式求解和滑动窗口最大值的计算。
摘要由CSDN通过智能技术生成

栈与队列

1.理论基础

stack:先进后出
queue:先进先出

232 用栈实现队列

栈是先进后出,队列是先进先出。
所以我们可以用两个栈来表示队列。一个栈表示进,另一个栈表示出

class MyQueue {

    Stack<Integer> stackIn;
    Stack<Integer> stackOut;

    public MyQueue() {
        stackIn = new Stack<>();//负责进栈
        stackOut = new Stack<>();//负责出栈
    }
    
    public void push(int x) {
        stackIn.push(x);
    }
    
    public int pop() {
        dumpstackIn();
        return stackOut.pop();
    }
    
    public int peek() {
        dumpstackIn();
        return stackOut.peek();
    }
    
    public boolean empty() {
        return stackIn.isEmpty() && stackOut.isEmpty();
    }

    private void dumpstackIn(){
    	//如果栈out还没有出完,那么就不能往栈out里放元素了。
        if(!stackOut.isEmpty()) return;
        while(!stackIn.isEmpty()){
            stackOut.push(stackIn.pop());
        }
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

225. 用队列实现栈

在Java中,Queue是一个接口,底层是通过链表实现的。
Deque:双端队列,指允许两端都可以进行入队和出队操作的队列,意为double ended queue。 其通过ArrayDeque/LinkedList初始化实现.

ArrayDeque和LinkedList这两者底层,一个采用数组存储,一个采用链表存储;

数组存储,容量不够时需要扩容和数组拷贝,通常容量不会填满,会有空间浪费;

链表存储,每次push都需要new Node节点,并且node节点里面有prev和next成员,也会有额外的空间占用。

class MyStack {

    Queue<Integer> queue1;
    Queue<Integer> queue2; 

    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        queue2.offer(x);//先放在辅助队列中
        while(!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }

        Queue<Integer> queueTemp;
        queueTemp = queue1;
        queue1 = queue2;
        queue2 = queueTemp;
    }
    
    public int pop() {
        return queue1.poll();
    }
    
    public int top() {
        return queue1.peek();
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

20 有效的括号

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();

        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i) == '('){
                deque.push(')');
                
            }else if(s.charAt(i) == '{'){
                deque.push('}');
            }else if(s.charAt(i) == '['){
                deque.push(']');
            }else if(deque.isEmpty() || deque.pop() != s.charAt(i)){
                return false;
            }
        }

        return deque.isEmpty();
        
    }
}

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

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

解析:可以采用deque,但是最后在返回结果的时候要注意顺序,

class Solution {
    public String removeDuplicates(String s) {

        ArrayDeque<Character> deque = new ArrayDeque<>();

        for(char ch : s.toCharArray()){
            if(deque.isEmpty()){
                deque.push(ch);
            }else if(deque.peek() == ch){
                deque.pop();
            }else{
                deque.push(ch);
            }
        }
        String res = "";
        //注意
        while(!deque.isEmpty()){
            res = deque.pop() + res;
        }
        return res;
    }
}

150. 逆波兰表达式

首先,我们先明确什么是逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。

平常使用的算式则是一种中缀表达式,如(1 + 2) * (3 + 4)

该算式的逆波兰表达式写法为:((1 2 +)(3 4 +)*)

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<>();
        int res = 0;

        for(String s : tokens){
            if("+".equals(s)){
                stack.push(stack.pop() + stack.pop());//leetcode这里报错,
            }else if("-".equals(s)){
                res = stack.pop() - stack.pop();
                stack.push(res);
            }else if("*".equals(s)){
                res = stack.pop() * stack.pop();
                stack.push(res);
            }else if("/".equals(s)){
                int temp = stack.pop();
                res = stack.pop() / temp;
            }else{
                stack.push(Integer.valueOf(s));
            }

        }

        return res;
    }
}

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 1){
            return nums;
        }
        int len = nums.length - k + 1;
        //存放结果元素的数组
        int[] res = new int[len];
        int num = 0;
        //自定义队列
        MyQueue myQueue = new MyQueue();
        //先将前k的元素放入队列
		//将最大的元素放在最前面,保持单调递减
        for(int i = 0; i < k ; i++){
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();
        for(int i = k; i < nums.length; i++){
            //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
            myQueue.poll(nums[i - k]);
            //滑动窗口加入最后面的元素
            myQueue.add(nums[i]);
            //记录对应的最大值
            res[num++] = myQueue.peek();
        }

        return res;

    }
}
class MyQueue{
    Deque<Integer> deque = new LinkedList<>();

    //当弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
    //同时判断队列当前是否为空
    void poll(int val){
        if(!deque.isEmpty() && val == deque.peek()){
            deque.poll();
        }
    }

    //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    //保证队列元素单调递减
    //比如此时队列元素3,1,2将入队,比1大,所以1弹出,此时队列3,2
    void add(int val){
        while(!deque.isEmpty() && val > deque.getLast()){
            deque.removeLast();
        }
        deque.add(val);
    }

    //队列队顶元素始终为最大值
    int peek(){
        return deque.peek();
    }
}

347. 前k个高频元素

/**Comoarator接口说明
    返回负数,形参中第一个参数排在前面;返回正数,形参中第二个参数排在前面
    对于队列:排在前面意味着往队头里靠
    对于堆(使用PriorityQueue实现):从队头到队尾按从小到大排就是最小堆(小顶堆)
                                    从队头到队尾按从大到小排就是最大堆(大顶堆)-->队头元素相当于对堆的根结点


 */

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //基于大顶堆实现
        Map<Integer, Integer> map = new HashMap<>();
        for(int num : nums){
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        //在优先队列中存储二元组(num, cnt),cnt表示元素值num在数组中的出现次数
        //出现次数按从队头到队尾的顺序是从大到小排,出现次数最多的是在队头(相当于大顶堆)
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair2[1] - pair1[1]);
        for(Map.Entry<Integer, Integer> entry : map.entrySet()){//大顶堆需要对所有的元素进行排序
            pq.add(new int[] {entry.getKey(), entry.getValue()});
        }
        int[] ans = new int[k];
        for(int i = 0; i < k; i++){
            ans[i] = pq.poll()[0];
        }
        return ans;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值