1. 逆波兰表达式求值
150. 逆波兰表达式求值https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["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
解题思路
这是一个后缀表达式的计算问题,后缀表达式是依靠栈进行计算的,数字就入栈,遇到运算符就出栈两次进行计算,计算后的值重新入栈。
边界值分析:
除法向0截断,也就是说不考虑小数;不含除0运算,可以不进行相应的if判断;32位整数表示,使用int类型就够了。
代码
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<>();
for (String token : tokens) {
if ("+".equals(token)) {
stack.push(stack.pop() + stack.pop());
} else if ("-".equals(token)) {
stack.push(-stack.pop() + stack.pop());
} else if ("*".equals(token)) {
stack.push(stack.pop() * stack.pop());
} else if ("/".equals(token)) {
int temp = stack.pop();
stack.push(stack.pop() / temp);
} else {
stack.push(Integer.valueOf(token));
}
}
return stack.pop();
}
}
2. 滑动窗口最大值
239. 滑动窗口最大值https://leetcode.cn/problems/sliding-window-maximum/description/
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
解题思路
需要找到一个滑动窗口每一个时刻的最大值,滑动窗口这种对首尾进行操作,不对中间进行操作,很适合队列。并且,只需要滑动窗口的最大值,所以不用在队列中维护全部的数。那么究竟需要维护多少呢,一个max,一个第二大的值second。要保证当最大值被弹出后,我依旧能够知道剩余部分的最大值是多少。
所以对于每一个值add进去的时候,把小于等于nums[i]的数全都弹出来。队列变成了一个单调队列。每次peek就是最大值。新加入的问题解决了,新弹出的问题不好解决。因为单纯的值是无法定位位置的,我无法感知到我这个最大值是不是应该到了要弹出的时间。
是不是要弹出取决于数组索引,而由数组索引可以得到值,所以改为存数组索引在队列内,比较的时候还是比较索引位置的值。
代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> queue = new LinkedList<>();
int[] max = new int[nums.length - k + 1];
int index = 0;
for (int i = 0; i < nums.length; i++) {
if(!queue.isEmpty()&&queue.peek()<i-k+1) {
queue.poll();
}
while (!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) {
queue.pollLast();
}
queue.add(i);
if (i >= k - 1) {
max[index++] = nums[queue.peek()];
}
}
return max;
}
}
3. 前 K 个高频元素
347.前 K 个高频元素https://leetcode.cn/problems/top-k-frequent-elements/给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
解题思路
对于一个算法题的思考,不能够完完全全的陷入到问题本身,包括写业务代码也一样。需要进行抽象,抽象的能力很重要,要把一个东西抽象出一些关键的部分。
这道题,找出频率最高的前k个元素,涉及到频率,还涉及到数据本身,还有频率的排序,需要遍历,遍历途中需要记录频率,所以需要哈希表,最后还涉及到排序。
现在问题的关键在于排序的选择上了,因为题目只要求前k个,所以全部进行排序一定不是最优的。有没有什么办法只取前k个呢?答案是堆。
大根堆无法只维持k个,还是需要维持n个,小根堆可以满足需求。对于超过k个之后的元素,如果需要加入就弹出堆头。
代码
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
PriorityQueue<int[]> queue = new PriorityQueue<>((o1, o2) -> {
return o1[1] - o2[1];
});
int[] res = new int[k];
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
map.forEach((key, value) -> {
if (queue.size() < k) {
queue.add(new int[] { key, value });
} else {
if (queue.peek()[1] < value) {
queue.poll();
queue.add(new int[] { key, value });
}
}
});
for (int i = k - 1; i >= 0; i--) {
res[i] = queue.poll()[0];
}
return res;
}
}