代码随想录-暑假算法第五天(栈和队列篇)

代码随想录-暑假算法第五天(栈和队列篇)

1.用栈实现队列

力扣题目链接(opens new window)

使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

示例:

MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false

说明:

  • 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

#算法公开课

《代码随想录》算法视频公开课 (opens new window)栈的基本操作! | LeetCode:232.用栈实现队列 (opens new window),相信结合视频再看本篇题解,更有助于大家对本题的理解

#思路

这是一道模拟题,不涉及到具体算法,考察的就是对栈和队列的掌握程度。

使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。

题解



import java.util.ArrayDeque;

class MyQueue {
    //使用两个栈模拟队列

    //用于交换栈里面的元素
    ArrayDeque<Integer> s1 ;
    //用于向队列尾部添加元素
    ArrayDeque<Integer> s2 ;

    public MyQueue() {
        s1 =  new ArrayDeque<>();
        s2 = new ArrayDeque<>();
        
    }

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

    public int pop() {
        //只要s1栈里面不为空 那么栈顶第一个元素就是队列的头元素
        if(s1.isEmpty()){
            //s1为空 那么把s2里面的元素全部添加到s1里面
            while(!s2.isEmpty()){
                Integer pop = s2.pop();
                s1.push(pop);
            }
        }
        //s1不为空
        return s1.pop();
    }

    public int peek() {

        //只要s1栈里面不为空 那么栈顶第一个元素就是队列的头元素
        if(s1.isEmpty()){
            //s1为空 那么把s2里面的元素全部添加到s1里面
            while(!s2.isEmpty()){
                Integer pop = s2.pop();
                s1.push(pop);
            }
        }
        //s1不为空
        return s1.peek();
        
    }

    public boolean empty() {
        if(s1.isEmpty() && s2.isEmpty()){
            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();
 */

2. 有效的括号

力扣题目链接(opens new window)

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

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。

示例 1:

  • 输入: “()”
  • 输出: true

示例 2:

  • 输入: “()[]{}”
  • 输出: true

示例 3:

  • 输入: “(]”
  • 输出: false

示例 4:

  • 输入: “([)]”
  • 输出: false

示例 5:

  • 输入: “{[]}”
  • 输出: true

题解



import java.util.ArrayDeque;

class Solution {
    public boolean isValid(String s) {
        //使用栈进行解题
        ArrayDeque<Character> arrayDeque = new ArrayDeque<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            //左 添加到栈里面
            if (c == '(') {
                arrayDeque.push(')');
            }else if(c == '['){
                arrayDeque.push(']');
            }else if (c == '{'){
                arrayDeque.push('}');
            }else {
                //右 取出栈顶元素和这个右符号进行判断
                if( !arrayDeque.isEmpty() && arrayDeque.peek() == c){
                    arrayDeque.pop();
                }else {
                    return false;
                }
            }
        }
        if(arrayDeque.isEmpty()){
            return true;
        }else{
            return false;
        }
    }
}

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

力扣题目链接(opens new window)

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

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

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

示例:

  • 输入:“abbaca”
  • 输出:“ca”
  • 解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

提示:

  • 1 <= S.length <= 20000
  • S 仅由小写英文字母组成。

题解



import java.util.ArrayDeque;

class Solution {
    public String removeDuplicates(String s) {
        ArrayDeque<Character> arrayDeque = new ArrayDeque<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(!arrayDeque.isEmpty() && arrayDeque.peek()!=null && c == arrayDeque.peek()){
                //将栈顶的元素移除
                arrayDeque.pop();
                
                continue;
            }else{
                //添加到栈里面
                arrayDeque.push(c);
            }
        }
        //最后遍历拼接即可   (要反着拼接)
        String result = "";
        while(!arrayDeque.isEmpty()){
            result =  arrayDeque.pop() + result ;
        }
        return result;
    }
}

4. 逆波兰表达式求值

力扣题目链接(opens new window)

根据 逆波兰表示法,求表达式的值。

有效的运算符包括 + ,  - ,  * ,  / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

  • 输入: [“2”, “1”, “+”, “3”, " * "]
  • 输出: 9
  • 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

  • 输入: [“4”, “13”, “5”, “/”, “+”]
  • 输出: 6
  • 解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

  • 输入: [“10”, “6”, “9”, “3”, “+”, “-11”, " * ", “/”, " * ", “17”, “+”, “5”, “+”]

  • 输出: 22

  • 解释:该算式转化为常见的中缀算术表达式为:

    ((10 * (6 / ((9 + 3) * -11))) + 17) + 5       
    = ((10 * (6 / (12 * -11))) + 17) + 5       
    = ((10 * (6 / -132)) + 17) + 5     
    = ((10 * 0) + 17) + 5     
    = (0 + 17) + 5    
    = 17 + 5    
    = 22    
    

    是一种后缀表达式,所谓后缀就是指运算符写在后面。

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

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

逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。

  • 适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。

题解



import java.util.ArrayDeque;

class Solution {
    public int evalRPN(String[] tokens) {
        //使用栈解决问题
        ArrayDeque<Integer> arrayDeque = new ArrayDeque<>();
        for (String token : tokens) {
            //解题思路: 如果是数字就压入栈 是符号就取出栈顶(不仅要拿,还要移除)的两个元素进行计算,然后把计算的结果在压入栈里面
            if(token.equals("+")){
                int n1 = arrayDeque.pop();
                int n2 = arrayDeque.pop();
                int number = n1+n2;
                arrayDeque.push(number);
            }else if(token.equals("-")){
                int n1 = arrayDeque.pop();
                int n2 = arrayDeque.pop();
                //减号和/号要特殊处理
                int number = n2-n1;
                arrayDeque.push(number);
            }else if(token.equals("*")){
                int n1 = arrayDeque.pop();
                int n2 = arrayDeque.pop();
                int number = n1*n2;
                arrayDeque.push(number);
            }else if(token.equals("/")){
                int n1 = arrayDeque.pop();
                int n2 = arrayDeque.pop();
                int number = n2/n1;
                arrayDeque.push(number);
            }else {
                //是数字
                int i = Integer.parseInt(token);
                arrayDeque.push(i);
            }
        }
        //返回结果
        return arrayDeque.pop();
    }
}

5.前 K 个高频元素

力扣题目链接(opens new window)

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

  • 输入: nums = [1,1,1,2,2,3], k = 2
  • 输出: [1,2]

示例 2:

  • 输入: nums = [1], k = 1
  • 输出: [1]

提示:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O ( n log ⁡ n ) O(n \log n) O(nlogn) , n 是数组的大小。
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
  • 你可以按任意顺序返回答案。

题解

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
 int[] result = new int[k];
        // key : 数字; value :出现到频次
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int curNum = nums[i];
            map.put(curNum, map.getOrDefault(curNum, 0) + 1);
        }

        // 根据 value 进行排序
        ArrayList<Map.Entry<Integer, Integer>> list = new ArrayList(map.entrySet());
        Collections.sort(list, (a, b) -> {
            return b.getValue() - a.getValue();
        });

        for (int i = 0; i < k; i++) {
            result[i] = list.get(i).getKey();
        }

        // System.out.println(JSONUtil.toJsonStr(result));
        return result;
    }
}


Map<Integer,Integer> map = new HashMap<>(); //key为数组元素值,val为对应出现次数
        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++) { //依次从队头弹出k个,就是出现频率前k高的元素
            ans[i] = pq.poll()[0];
        }
        return ans;

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值