代码随想录刷题记录:栈和队列篇

前言

这篇就到栈和队列啦~~~加油加油!奥利给!

232. 用栈实现队列

在这里插入图片描述
思路分析:
卧槽这个我一遍就过了,感觉只有这么简单了,只要明白栈和队列的特性就可以了。具体看代码注释就没什么问题了。

代码如下:

class MyQueue {
    //我们需要先声明两个栈
    Stack<Integer> stack1;
    Stack<Integer> stack2;

    //在构造函数中对两个栈进行初始化
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void push(int x) {
        stack1.push(x); //往stack1里面压栈
    }
    
    public int pop() {
        //因为栈先进后出,队列是先进先出
        //所以我们将stack1中的数据全部又放入stack2中
        while(stack1.size() > 0){
            stack2.push(stack1.pop());
        }
        //现在将stack2中pop出去第一个即最先进入队列中的值
        int res = stack2.pop();
        //然后再把这些值又放回stack1中
        while(stack2.size() > 0){
            stack1.push(stack2.pop());
        }
        //返回该值
        return res;
    }
    
    public int peek() {
        //因为栈先进后出,队列是先进先出
        //所以我们将stack1中的数据全部又放入stack2中
        while(stack1.size() > 0){
            stack2.push(stack1.pop());
        }
        //将stack2中pop出去第一个,用res记录下来
        int res = stack2.pop();
        //因为peek方法只是查看
        //所以我们还需要将这个值再放回stack2中
        stack2.push(res);
        //然后再把这些数据再放回stack1中
        while(stack2.size() > 0){
            stack1.push(stack2.pop());
        }
        //返回该值
        return res;
    }
    
    public boolean empty() {
        //这个就很简单啦
        //因为我们存数据的栈只是stack1
        //所以我们只要判断stack1的大小如果为0则队列就为空
        //否则不为空
        if(stack1.size() == 0) return true;
        else return false;
    }
}

/**
 * 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. 用队列实现栈

题目描述:
在这里插入图片描述
思路分析:
水题,就和之前的栈模拟队列一样,只要明白栈是先进后出而队列是先进先出的特性就好做,代码中注释非常清楚。

代码如下:

class MyStack {
    //我们先需要声明两个队列
    Queue<Integer> queue1;
    Queue<Integer> queue2;

    public MyStack() {
        //在构造器中实例化着两个队列
        //Queue为抽象类无法实例化
        //所以Java中队列一般都用LinkedList来实现
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        //用queue1来存储数据
        queue1.add(x);
    }
    
    public int pop() {
        //因为栈先进后出,队列先进先出
        //所以我们先清空queue1除了最后一个数据的其他所有数据
        //这些被清空的数据暂时放入queue2中
        while(queue1.size() > 1){
            queue2.add(queue1.remove());
        }
        //用res保存queue1的独苗
        int res = queue1.remove();
        //然后再将之前的数据放回queue1中
        while(queue2.size() > 0){
            queue1.add(queue2.remove());
        }
        //返回该值
        return res;
    }
    
    public int top() {
        //因为栈先进后出,队列先进先出
        //所以我们先清空queue1除了最后一个数据的其他所以数据
        //这些被清空的数据先放入queue2中
        while(queue1.size() > 1){
            queue2.add(queue1.remove());
        }
        //用res先保存下queue1的独苗
        int res = queue1.remove();
        //取到值后因为top只是查看队首元素
        //所以我们再刚刚移除的元素再放回queue2中凑完整
        queue2.add(res);
        //然后再将一整个这些数据放回queue1中
        while(queue2.size() > 0){
            queue1.add(queue2.remove());
        }
        //返回该值
        return res;
    }
    
    public boolean empty() {
        //因为只有queue1用来存数据
        //所以如果queue1长度为0的话,则队列为空返回true
        //否则返回false
        if(queue1.size() == 0) return true;
        else return false;
    }
}

/**
 * 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. 有效的括号

题目描述:
在这里插入图片描述
思路分析:
如题中代码注释部分。

代码如下:

class Solution {
    public boolean isValid(String s) {
        //因为栈的特性,所以对称匹配类的题目用栈很好做
        //但还是有一定的技巧性,这个要多做多练就能get的
        //创建一个栈
        Stack<Character> stack = new Stack<>();
        //遍历字符串s
        for(int i=0; i<s.length(); i++){
            //如果当前字符为左括号,那么我们往栈中加入对应的右括号
            //如果当前字符为右括号,则会对栈是否为空或者栈顶字符是否与其相同进行判断
            //如果栈为空,说明没有与之对应的左括号,出现不够匹配的情况返回false
            //或者栈顶字符与当前字符不相同,这时肯定会出现匹配矛盾的情况,return false
            //最后如果是栈顶字符和当前字符相同的话,匹配上了,则删除该栈顶字符即可
            if(s.charAt(i) == '(') stack.push(')');
            else if(s.charAt(i) == '{') stack.push('}');
            else if(s.charAt(i) == '[') stack.push(']');
            // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
            // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
            else if(stack.isEmpty() || stack.peek() != s.charAt(i)) return false;
            else stack.pop();        
        }
        //第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
        return stack.isEmpty();
    }
}

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

在这里插入图片描述
思路分析:
其实就用了个栈,没有多难,看代码注释就能看明白。

代码如下:

class Solution {
    public String removeDuplicates(String s) {
        Stack<Character> stack = new Stack<>();
        for(int i=0; i<s.length(); i++){
            //当前元素和栈顶元素相同,所以应该删除
            if(stack.size()>0 && s.charAt(i) == stack.peek()){
                stack.pop();
            }else{//如果不相同,那么加入栈中
                stack.push(s.charAt(i));
            }
        }
        //最后将栈中所有的数据转化为字符串即可
        StringBuilder sb = new StringBuilder();
        while(stack.size()>0){
            sb.append(stack.pop());
        }
        //因为栈是先进后出,所以还需要反转一下该字符串再进行返回
        String str = sb.toString();
        StringBuilder sbb = new StringBuilder(str).reverse();
        return sbb.toString();
    }
}

150. 逆波兰表达式

题目描述:
在这里插入图片描述
思路分析:
也不是很难,就是只要明白什么叫逆波兰表达式就很好做,代码注释中写的很明白了。

代码如下:

class Solution {
    public int evalRPN(String[] tokens) {
        //题目不难,关键是要指导啥是逆波兰表达式
        //逆波兰表达式:是一种后缀表达式
        //所谓后缀就是把运算符写在后面
        //波兰表达式同样是栈的典型应用
        //先创建一个栈
        Stack<Integer> stack = new Stack<>();
        //循环判断
        for(int i=0; i<tokens.length ;i++){
            //如果遇到的是运算符,那么就要弹出对应的两个数字进行运算
            if(tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*") || tokens[i].equals("/")){
                //取到弹出栈的两个值
                int a = stack.pop();
                int b = stack.pop();
                //下面分情况进行讨论
                if(tokens[i].equals("+")) {
                    stack.push(a + b);
                }
                //因为栈是先进后出,所以减数和被减数需要换个位置进行减法操作
                if(tokens[i].equals("-")) {
                    stack.push(b - a);
                }
                if(tokens[i].equals("*")) {
                    stack.push(a * b);
                }
                //除法也要替换,原因同上
                if(tokens[i].equals("/")) {
                    stack.push(b / a);
                }
            }else{
                //如果当前字符串为数字的话,就存入栈中
                stack.push(Integer.valueOf(tokens[i]));
            }
        }
        //最后栈肯定会存在唯一一个值,就是我们的运算结果
        //返回即可
        return stack.pop();
    }
}

239. 滑动窗口最大值

题目描述:
在这里插入图片描述
题目分析:
其实我也不是很明白…暴力是可以解的(在牛客上不会超时),但是在LeetCode上会超时,看了很多题解说是用单调队列来做,我看的不是很明白(倒懂不懂的)…等后面思维提升了复盘的时候再来重新分析吧。

代码如下:

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 {
    //刷题多了刷到一个技巧
    //用比如LinkedList或者ArrayList时
    //最好前面类型也用LinkedList或者ArrayList
    //别用List接口或者抽象类之类的
    LinkedList<Integer> deque = new LinkedList<>();
    //弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
    //同时判断队列当前是否为空
    void poll(int val) {
        if (!deque.isEmpty() && val == deque.peek()) {
            deque.remove();
        }
    }
    //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    //保证队列元素单调递减
    //比如此时队列元素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个高频元素

在这里插入图片描述
思路分析:
遇到这种有一一对应关系的题目,肯定都会想到HashMap来做,但我做这个时其实忘记了一个API,就是HashMap如何提取一对一对的KV键值对,用entrySet即可,得记下来,如下:

for(Map.Entry<Integer,Integer> entry : map.entrySet()){
            if(entry.getValue() > maxTimes){
                maxTimes = entry.getValue();
            }
        }

还可以使用entry.getKey()拿到Key键。
然后这种题还有个小技巧,在做题时可以留意一下,注释中会有提到。

代码如下:

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //这个感觉可以用哈希表做
        //map的key键存元素值,value值存出现次数(即频率)
        HashMap<Integer,Integer> map = new HashMap<>();
        //创建结果数组
        int[] res = new int[k];
        
        //遍历计数即可
        for(int i=0; i<nums.length; i++){
            if(map.containsKey(nums[i])){
                map.put(nums[i],map.get(nums[i])+1);
            }else{
                map.put(nums[i],1);
            }
        }
        //接下来找到value值最大的前k个key键组成数组返回即可
        
        int maxTimes = 0; //计数器,记录出现频率最高的元素的出现次数
        //找出出现次数最多的元素出现的次数
        for(Map.Entry<Integer,Integer> entry : map.entrySet()){
            if(entry.getValue() > maxTimes){
                maxTimes = entry.getValue();
            }
        }

        //按出现次数从大到小往结果数组中添加元素
        while(k>0){
            for(Map.Entry<Integer,Integer> entry : map.entrySet()){
                if(entry.getValue() == maxTimes){
                    k--;
                    res[k] = entry.getKey();
                }
            }
            //每回都将maxTimes自减1,逐个比较
            //这样一定能找到出现频率最高的前k个元素而不会遗漏
            //这个小技巧可以记忆一下
            maxTimes--;   
        }   
        return res;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在地球迷路的怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值