1、题目1:150-逆波兰表达式求值
类型:中等
给你一个字符串数组
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
思路:
1、什么是逆波兰表达式?
也叫后缀表达式,我们常用的是中缀表达式,例如(1+2)*(3+4)
换成二叉树长这样,而二叉树后缀表达式就是我们表达式:12+34+x
暂时无法在飞书文档外展示此内容
后缀表达式顺序处理字符串,遇到计算符号从栈出取出来计算
编码比较容易
public static int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (String token : tokens) {
if(token.equals("+")){
Integer pop = stack.pop();
Integer pop1 = stack.pop();
stack.add(pop1+pop);
}else if(token.equals("-")){
Integer pop = stack.pop();
Integer pop1 = stack.pop();
stack.add(pop1 -pop);
}else if(token.equals("*")){
Integer pop = stack.pop();
Integer pop1 = stack.pop();
stack.add(pop*pop1);
}else if(token.equals("/")){
Integer pop = stack.pop();
Integer pop1 = stack.pop();
stack.add(pop1/pop);
}else{
stack.add(Integer.valueOf(token));
}
}
return stack.pop();
}
2、题目2:239-滑动窗口最大值
类型:困难
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
进阶:你能在线性时间复杂度内解决此题吗?
思路:
滑动窗口的移动过程,很像一个队列,队列里始终维护窗口里的值。
难点是如何求一个区间的最大值,暴力方法,遍历一遍的过程每次从窗口中再找到最大的数值,这样很明显是O(n*k)的算法。
而想到排序,可能会想到大顶堆来存放这个窗口里的k个数字,这样就可以知道最大的最大值是多少了,但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了,所以不能用大顶堆。
这个队列应该有三个方法:
- Pop
- Push
- getMax
每次窗口移动的时候,调用pop移除元素,push 放入元素,然后通过 getMax返回最大值。
然后再分析一下,队列里的元素一定是要排序的,而且要最大值放在出队口,要不然怎么知道最大值呢。
但如果把窗口里的元素都放进队列里,窗口移动的时候,队列需要弹出元素。
那么问题来了,已经排序之后的队列 怎么能把窗口要移除的元素(这个元素可不一定是最大值)弹出呢。
大家此时应该陷入深思.....
其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。
单调队列,即单调递减或单调递增的队列.
public static 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();
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<Integer>();
// 弹出元素,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
// 同时判断队列当前是否为空
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();
}
}
3、题目3:前k个高频元素
类型:中等
首先统计元素出现的频率,这一类的问题可以使用map来进行统计。
然后是对频率进行排序,这里我们可以使用一种 容器适配器就是优先级队列。
大/小顶堆的应用, 在C++中就是优先级队列,本题是 大数据中取前k值 的经典思路堆的数据结构来处理。
什么是堆呢?
堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
使用小顶堆,不断把最小值排出去。key 是元素,value 是频率。
本题我们就要使用优先级队列来对部分频率进行排序。
为什么不用快排呢, 使用快排要将map转换为vector的结构,然后对整个数组进行排序, 而这种场景下,我们其实只需要维护k个有序的序列就可以了,所以使用优先级队列是最优的。
public int[] topKFrequent(int[] nums,int k){
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num,map.getOrDefault(num,0) +1);
}
// 在优先队列中存储二元组(num,cnt),cnt 表示元素值 num 在数组中的出现次数
// 出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(小顶堆)
// 即使用第二个频率进行排序
PriorityQueue<int[]> queue = new PriorityQueue<>(((o1, o2) -> o1[1]-o2[1]));
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(queue.size()<k){
queue.add(new int[]{entry.getKey(),entry.getValue()});
}else{
//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
if(entry.getValue() > queue.peek()[1]){
queue.poll(); //弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
queue.add(new int[]{entry.getKey(), entry.getValue()});
}
}
}
int[] res = new int[k];
for (int i = k-1; i >=0 ; i--) {
res[i] = queue.poll()[0];
}
return res;
}