逆波兰表达式求值
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。注意:
- 有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList();
for (String token : tokens) {
char c = token.charAt(0);
if (!isOpe(token)) {
stack.addFirst(stoi(token));
}else if (c == '+') {
stack.push(stack.pop() + stack.pop());
}else if (c == '-') {
int num1 = stack.pop();
int num2 = stack.pop();
stack.push(num2 - num1);
}else if (c == '*') {
stack.push(stack.pop() * stack.pop());
}else {
int num1 = stack.pop();
int num2 = stack.pop();
stack.push(num2 / num1);
}
}
return stack.pop();
}
private boolean isOpe(String s) {
return s.length() == 1 && s.charAt(0) < '0' || s.charAt(0) > '9';
}
private int stoi(String s) {
return Integer.valueOf(s);
}
}
逆波兰式是比较典型的使用栈来解决的问题,这思路比较容易想到,但是题中还是有几处容易出错:
- 首先我们要知道,将字符串转化成Intege类型才能操作算术运算。
- String类型的字符串可以使用增强for来进行遍历,每次遍历一个字符。
- 什么情况下入栈(不是单纯的不等于±*/),而是需要判定长度为1,数字为1到9时入栈。
- 减法和除法需要注意运算顺序。
- Deque和Stack的区别到底是什么。
Deuqe,即“double ended queue”的缩写,是Java中的双端队列集合类型。
具备普通队列FIFO的功能,也具备了Stack的LIFO功能。
Deque可以由ArrayDeuqe或者LinkedList实现。
Vector 很多方法都用了synchronized修饰,线程安全,但效率太低,已被弃用。
Stack 继承Vector,暴露了set/get方法,可以进行随机位置的访问,与与Stack 的设计理念相冲突
滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1) {
return nums;
}
MyQueue que = new MyQueue();
int len = nums.length;
int index = 0;
int[] result = new int[len - k + 1];
for (int i = 0; i < k; i++) {
que.push(nums[i]);
}
result[index++] = que.front();
for (int i = k; i < len; i++) {
que.pop(nums[i - k]);
que.push(nums[i]);
result[index++] = que.front();
}
return result;
}
}
public class MyQueue {
// 使用Deque
Deque<Integer> que = new LinkedList<>();
// 定义出队列操作
//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
//同时判断队列当前是否为空
public void pop (int value) {
if (!que.isEmpty() && value == que.peek()) {
que.poll();
}
}
// 定义进队操作
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
public void push(int value) {
while (!que.isEmpty() && value > que.getLast()) {
que.removeLast();
}
que.add(value);
}
// 取出队列最前面的值,但是不弹出
public int front() {
return que.peek();
}
}
这道题被定义为一个困难题,但是仔细分析后, 是可以解决的。
需要注意的点:
- 使用Deque的队列,并且使用实现这个接口的类LinkedList<E>来实现。
- 方法的使用,这里面方法实在是太多了,怎么区分与选择?
- peek()、poll()、removeLast()
了解我们的单调队列:
我们需要自己造一个单调队列,队列里面的元素需要单调递减,存放的是我们一个滑动窗口中的元素的一个单调递减顺序,目前主要的注意力就是在进队,如果发现进队的元素大于队列队尾元素,则就尾元素出队,则直到进队这个元素的地方,前面窗口的中元素都小于该元素,所以可以将对列清空,在入队该元素。
出队操作,考虑到什么时候什么时候需要出队,就是该队首的元素已经滑到窗口外了,不属于滑动窗口中最大的元素了。
前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
此题基本上就是全懵状态了:
- 不熟悉map的使用,特别是entrySet()这个方法的返回结果。
- 没有使用过堆来解决问题,堆的原理是懂得,就是api不熟悉。
- PriorityQueue不熟悉,包括其中的方法不熟悉。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] result = new int[k];
// 使用HashMap来统计每个元素出现的次数
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
// 根据map的value值正序排,相当于一个小顶堆
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
for (Map.Entry<Integer, Integer> entry : entries) {
queue.offer(entry);
if (queue.size() > k) {
queue.poll();
}
}
for (int i = k - 1; i >= 0; i--) {
result[i] = queue.poll().getKey();
}
return result;
}
}
该题基本上是属于抄的一个状态,但是我们要清楚的是什么时候需要使用堆这种方法,并且合理的选择大顶堆和小顶堆。PriorityQueue就是实现堆的一种操作我们也需要了解。
接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
此题解法比较多,目前就实现了双指针,然后还可以使用动规、单调栈来解题,个人认为双指针解法完全在于逻辑问题,是达不到困难的程度的。
class Solution {
public int trap(int[] height) {
int res = 0;
for (int i = 0; i < height.length; i++) {
// 第一个和最后一个必没有存水,所以不计算
if (i == 0 || i == height.length - 1) {
continue;
}
int lheight = height[i];
int rheight = height[i];
// 寻找左边最高的柱子,没有高于它的情况, lheight就是它本身的高度
for (int j = i - 1; j >= 0; j--) {
if (height[j] > lheight) {
lheight = height[j];
}
}
// // 寻找右边最高的柱子,没有高于它的情况, rheight就是它本身的高度
for (int j = i + 1; j <= height.length - 1; j++) {
if (height[j] > rheight) {
rheight = height[j];
}
}
res += Math.min(rheight, lheight) - height[i];
}
return res;
}
}
解题思路主要就是一列一列的求此列可以积的雨水量,怎么求该列积的雨水量呢?也就是需要分别找到该列左右最高的列,然后用最小值去减去该列的值,当该列的左右没有比他更小的,那么该列的值就是最小值,最后求得的值也是0.
这道题使用双指针法的时间和空间复杂度还是挺高的,后面来补充动规和单调栈的方法。