算法刷题Day11 - 栈与队列part2|LC.150 逆波兰表达式求值|LC.239 滑动窗口最大值|LC.347 前K个高频元素

LeetCode - 150. Evaluate Reverse Polish Notation 逆波兰表达式求值

什么是逆波兰表达式?逆波兰表达式也称为后缀表达式,我们平常写出来的数学表达式大多为中缀表达式,如(1+2) * (3+4)。如果把这个画成树状图,就可以遍历得出逆波兰表达式。 中缀表达式需要加括号才能正确运算,但是后缀表达式就可以不用括号表示,用顺序就能计算出正确结果,这个计算过程就是由栈来实现的。

解题思路:遍历一个逆波兰表达式,如果遇见数字则加入栈中,如果两个数字遇见了一个操作符,就在栈中消除合成一个数字,再加入到栈中,最后的结果也就是栈中的最后唯一一个元素。其实和1047题相邻字符删除有些类似,栈就很适合用来解决这类问题。同时需要注意的是因为逆波兰表达式的遍历方式是左右中,因此在减和除的操作应该是第二个减或除以第一个数字。

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> st = new Stack<>();
        for (String c : tokens) {
            if (c.equals("+")|| c.equals("-") || c.equals("*") || c.equals("/")) {
                //for operators
                int num1 = st.pop();
                int num2 = st.pop();
                int res = 0;
                switch (c) {
                    case "+" -> res = num1 + num2;
                    case "-" -> res = num2 - num1; //左右中遍历
                    case "*" -> res = num1 * num2;
                    case "/" -> res = num2 / num1;
                }
                st.add(res);
            } else {
                //for numbers
                st.add(Integer.parseInt(c));
            }
        }
        return st.pop();
    }
}

LeetCode - 239. Sliding Window Maximum 滑动窗口最大值

解题目标:给定一个数组以及滑动窗口大小k,求出每一个滑动窗口内的最大值

(待更新...)

LeetCode - 347. Top K Frequent Elements 前K个高频元素

解题目标:给定数组和正整数k,返回前k个高频元素。

解题思路:这道题有两个难点,一个是如何求每个元素出现的次数,还有就是如何对频率进行排序。对于频率的记录可以用Map结构,key存储元素,value存储出现次数。大顶堆和小顶堆可以用来求出前k个高频元素。Java中的优先级队列可以直接实现堆的逻辑。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //pq 默认为小顶堆,o2[1]-o1[1]则就是大顶堆
        PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o2[1] - o1[1]);
        HashMap<Integer, Integer> hm = new HashMap<>();
        int[] res = new int[k];
        //record frequencies to HashMap
        for (int num : nums) {
            hm.put(num, hm.getOrDefault(num, 0) + 1);
        }
        //add element (entry) to pq
        for (var x : hm.entrySet()) {
            int[] temp = new int[2];
            temp[0] = x.getKey();
            temp[1] = x.getValue();
            pq.offer(temp);
        }
        //get root
        for (int i = 0; i < k; i++) {
            res[i] = pq.poll()[0];
        }
        return res;
    }
}

在第一次写的时候用的是大顶堆,大顶堆的思路必须是加入所有的元素最后再依次poll到结果集中,但是我们无法确定元素的个数,但是小顶堆的思路可以在添加的时候同时剔除最小的元素,这样可以保证优先级队列的长度永远最大是k吗,堆排序的时间复杂度为logk,而大顶堆排序则需要logn。以下是小顶堆的实现代码:

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //pq 默认为小顶堆,o2[1]-o1[1]则就是大顶堆
        PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
        HashMap<Integer, Integer> hm = new HashMap<>();
        int[] res = new int[k];
        //record frequencies to HashMap
        for (int num : nums) {
            hm.put(num, hm.getOrDefault(num, 0) + 1);
        }
        //add element (entry) to pq
        for (var x : hm.entrySet()) {
            int[] temp = new int[2];
            temp[0] = x.getKey();
            temp[1] = x.getValue();
            pq.offer(temp);
            // once pq is longer than k, remove root element(smallest)
            if (pq.size() > k) {
                pq.poll();
            }
        }
        
        for (int i = k-1; i >= 0; i--) {
            res[i] = pq.poll()[0];
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值