剑指 Offer II 036. 后缀表达式【中等题】
思路:
遍历给定的字符串数组,将数字压入栈,遇到运算符则将栈顶的两个元素弹出进行运算后将结果再压入栈,题目保证给定的逆波兰表达式总是有效的,因此一定可以返回一个有效的答案,因此处理完之后栈中只会剩下最后的答案,将栈顶元素弹出作为答案返回即可。
代码:
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> deque = new ArrayDeque<>();
for (String token : tokens) {
char ch = token.charAt(0);
switch (ch){
case '+':
//将 + 前边的两个数弹出,相加之后压入栈
int n2 = deque.pop(),n1 = deque.pop();
deque.push(n1+n2);
break;
case '-':
if (token.length() == 1){
//将 - 前边的两个数弹出,相减之后入栈
int n4 = deque.pop(),n3 = deque.pop();
deque.push(n3-n4);
}else {
//这只是个负数,将其压入栈
deque.push(Integer.parseInt(token));
}
break;
case '*':
//将 * 前边的两个数弹出,相乘之后入栈
int a2 = deque.pop(),a1 = deque.pop();
deque.push(a1*a2);
break;
case '/':
//将 / 前边的两个数弹出,相除之后入栈
int a4 = deque.pop(),a3 = deque.pop();
deque.push(a3/a4);
break;
default:
//这是一个数据,将其转为int类型后压入栈
deque.push(Integer.parseInt(token));
}
}
//处理到最后栈中只会剩下一条数据,这就是最终结果,将其弹出返回即可
return deque.pop();
}
}
剑指 Offer II 037. 小行星碰撞【中等题】
思路:
从左到右遍历给定的行星数组,将行星压入栈,并判断行星的移动方向,遇见第一个右移的行星,将当前行星下标
+1
后退出循环。
从第一个右移行星右侧开始继续向右判断,如果待判断行星右移,则将其压入栈继续判断下一个行星,如果待判断行星左移,则将其与栈中的右移行星进行爆炸抵消:那么如何进行抵消呢?如果栈顶的右移行星,如果右移行星质量小于左移行星,则继续尝试取出栈顶的右移行星;如果右移行星质量大于左移行星,则重新将右移行星入栈,退出爆炸循环,继续判断下一个位置的行星;如果右移行星质量等于左移行星,则直接退出爆炸循环,继续判断下一个位置的行星。退出爆炸循环后,此时如果爆炸循环中没有直接退出到外层循环,继续判断下一个位置,那么此时栈顶行星一定是在左移,此时说明这个正在左移的当前行星质量实在太大,将所有的右移行星都炸掉了,此时需要从当前行星开始向右遍历行星数组,并将遍历到的行星入栈,直到重新找到一个右移的行星位置,将行星下标index
更新为新的右移行星下标+1
。
当所有行星遍历完成之后,此时栈中剩下的元素就是幸存的行星,新建一个int
类型数组remain
,长度等于栈的大小,将栈中的元素倒序取出,正序添加到remain
中,返回remain
。
代码:
class Solution {
public int[] asteroidCollision(int[] asteroids) {
int n = asteroids.length;
Deque<Integer> deque = new ArrayDeque<>();
int index = 0;
while (index < n){//寻找第一个右移的行星位置
int cur = asteroids[index++];
deque.push(cur);
if (cur > 0){
break;
}
}
while (index < n){
//获取现在要判断的行星 cur
int cur = asteroids[index];
if (cur > 0){//cur行星向右移动,不可能相撞,将其压入栈
deque.push(cur);
index++;
}else {//cur行星向左移动,需要与正在右移的pre行星进行碰撞判断
//是否判断下一个位置行星的标志位
boolean flag = false;
while (!deque.isEmpty() && deque.peek() > 0){
//获取现在正在右移的行星 pre
int pre = deque.pop();
if (pre < -cur){//pre爆炸
//正在右移的行星爆炸,左移行星继续碰撞上一个正在右移的行星
continue;
}
if (pre > -cur){//cur爆炸
//重新将pre入栈,退出爆炸循环,判断下一个位置的行星
deque.push(pre);
flag = true;
break;
}
if (pre == -cur){//一起爆炸
//直接退出爆炸循环,判断下一个位置的行星
flag = true;
break;
}
}
if (flag){//如果flag为true,说明需要判断下一个位置的行星,将index+1后进行下次判断
index++;
continue;
}
//此时栈顶行星正在左移,说明cur将左侧右移的行星全部消灭,将cur入栈之后需要在cur右侧重新寻找第一个右移的行星
while (index < n){
//求出cur右侧的行星
cur = asteroids[index++];
//将当前行星入栈
deque.push(cur);
if (cur > 0){//如果行星右移,我们找到了cur右侧第一个右移的行星,退出循环,否则继续寻找下一个右移行星
break;
}
}
}
}
int size = deque.size();
int[] remain = new int[size];
for (int i = 0; i < size; i++) {//将deque中的元素倒序取出并正序加入remain数组
remain[i] = deque.removeLast();
}
return remain;
}
}
剑指 Offer II 038. 每日温度【中等题】
思路:【deque单调栈】&【数组模拟单调栈】
思路都是一样的,正向遍历温度数组,当栈为空时将温度对应的下标压入栈,当栈不为空时,判断当前温度与栈顶温度的大小关系,如果小于等于栈顶温度,将当前温度对应的下标压入栈,如果大于栈顶温度,进入出栈循环,将当前栈顶温度对应的下标出栈,设为
peak
,将needDays
数组中栈顶下标位置设置为当前温度i
与栈顶温度peak
的差,即needDays[peak] = i - peak
,直到当前温度小于等于栈顶温度,或者栈为空时,退出出栈循环。
数组模拟单调栈代码源自官解下边评论区yukiyama
代码1:
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] needDays = new int[n];
Deque<Integer> deque = new ArrayDeque<>();
for (int i = 0; i < n; i++) {//正向遍历温度数组
//取出当前遍历到的温度
int t = temperatures[i];
//当栈不为空时,且当前温度 比 栈顶温度 大 时
while (!deque.isEmpty() && t > temperatures[deque.peek()]){
//取出栈顶温度对应的下标
int peak = deque.pop();
//将栈顶温度下标处的needDay数组元素置为 当前下标 - 栈顶下标
needDays[peak] = i - peak;
}
//当前温度小于栈顶元素,或者栈为空时,将当前温度的下标入栈
deque.push(i);
}
return needDays;
}
}
代码2:
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length,top = -1;
int[] needDays = new int[n],satck = new int[n];
for (int i = 0; i < n; i++) {//正向遍历温度数组
//取出当前遍历到的温度
int t = temperatures[i];
//当栈不为空时,且当前温度 比 栈顶温度 大 时
while (top != -1 && t > temperatures[satck[top]]){
//取出栈顶温度对应的下标
int peak = satck[top--];
//将栈顶温度下标处的needDay数组元素置为 当前下标 - 栈顶下标
needDays[peak] = i - peak;
}
//当前温度小于栈顶元素,或者栈为空时,将当前温度的下标入栈
satck[++top] = i;
}
return needDays;
}
}