链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
给定牛牛一个后缀表达式s,计算它的结果,例如,1+1对应的后缀表达式为1#1#+,‘#’作为操作数的结束符号。
其中,表达式中只含有‘+’、’-‘、’*‘三种运算,不包含除法。
本题保证表达式一定合法,且计算过程和计算结果的绝对值一定不会超过101810^{18}1018
示例1
输入
"1#1#+"返回值
2说明
1#1#+这个后缀表达式表示的式子是1+1,结果为2备注:
1≤表达式中操作数≤1091\leq表达式中操作数\leq10^91≤表达式中操作数≤109
1≤表达式长度≤1061\leq表达式长度\leq10^61≤表达式长度≤106
解题思路
因为刚刚才了解完栈这个数据结构,然后刷视频刷到了逆波兰表达式求值,然后就在牛客上找了题做一做。
这道题我是用数据结构栈来做的。
接触的第一道逆波兰表达式求值是在b站上刷视频刷到的,那个题目是力扣150题:
上面这道力扣题,我自己感觉输入相对来说较为友好,因为它是以字符串数组的形式给的,这就避免了"数字分家"的情况。
这两道题的解题思路本质思想基本相似,都是运用栈数据结构,然后遍历输入,遇到数字压入栈,遇到运算符时,将栈中元素弹出两次,然后进行计算,再把计算结果压入栈中,最后返回栈中第一个元素即可。
运用栈数据结构
我是直接用java内置的栈。
Stack<Long> stack = new Stack<>();
因为题中给的数据范围超过了int,所以我们泛型使用Long类型。(常听一些大佬说有爆范围、爆数据啥的,这块还不是特别了解,目前只知道超过int就得用longT__T)。
设置一个截取字符串的标记index,初始化为0
int index = 0;
这种方法是我看了若干个题解后,我理解起来比较容易的一种方法。(其他题解看的时候,由于能力较低,我确实有点没能完全get到代码的精髓,理解不是很透彻。)
因为牛客这题是以"#"为操作数的结束符号,而且输入数据的类型是字符串,与力扣那个字符串数组不同,字符串没法很智能的帮我们把数字、运算符都划分好,我们遍历的时候得到的数据都是单个单个的,所以不如直接利用”#“和index直接截取。
在此之前,自己有一些失败的操作:
1. 失败案例一:我用正则表达式,以"#"为分隔符把输入的字符串变成字符数组,当我自以为思路很妙的时候,提交之后直接就wa了,后来调试一番发现,当出现这种情况"12#+12-3"时,”+“和”12“没法分开。
2.失败案例二:还打算一个个遍历去判断数字到底是几位数,什么时候运算符后面是数字,没写几行代码,中道崩殂......
遍历输入的数据,找到数字,找到运算符
大致思路:遍历已经输入进来的数据,遇到数字压入栈,遇到运算符时,将栈中元素弹出两个,然后进行计算,再把刚刚的计算结果压入栈中,循环往复,直到遍历结束,然后返回栈中第一个元素。
for(int i = 0;i < str.length();i ++) {
char c = str.charAt(i);
if(c == '#') {
stack.push(Long.parseLong(str.substring(index,i)));
index = i + 1;
}
else if (c == '+') {
long b = stack.pop();
long a = stack.pop();
stack.push(a + b);
index = i + 1;
}
else if (c == '-') {
long b = stack.pop();
long a = stack.pop();
stack.push(a - b);
index = i + 1;
}
else if (c == '*') {
long b = stack.pop();
long a = stack.pop();
stack.push(a * b);
index = i + 1;
}
}
return stack.pop();
我们先遍历字符串,用字符c接受每次遍历到的单个字符。
当字符c是 "#" 时,说明我们输入完成了一个整数(但是位数不确定,可能只有个位,可能也有十位百位...)。那么怎么把这个整数给它完整的取出来呢?
第一次发现井号时,#的索引是i,那么[0,i-1]都是数字,所以我们用substring方法直接截取就行。截取到的数字,我们把它压入栈中。
因为题目说表达式一定合法,如果第一个字符就是运算符的话,那么进入后面的if语句时,栈是空的,调用pop会报错,所以感觉最开始的字符都是数字。所以第一次发现井号调用substring时,参数index = 0;截取完字符串,我们让index = i + 1;即让index表示"#“的下一个字符的索引。
后几次发现井号时,与第一次思路相同,只不过index每一次都要更新成i+1,substring中的参数与第一次发现井号不同,变成了(index,i),所谓的不同,即是此时的index不再等于0。
当碰到运算符时,根据碰到的运算符进入相应的if语句。
遇到运算符说明肯定有两个数据要用当前运算符进行运算,那两个数据就是前面刚刚被压入栈的两个数据,所以我们只需要操作栈弹出顶部两个元素,进行对应的操作即可。得到的结果我们再压入栈中,然后循环往复,进行下一次的运算或者当作结果输出。
提交的代码部分
public long legalExp (String str) {
// write code here
Stack<Long> stack = new Stack<>();
int index = 0;//用于后续截取字符串
for(int i = 0;i < str.length();i ++) {
char c = str.charAt(i);
if(c == '#') {
stack.push(Long.parseLong(str.substring(index,i)));
index = i + 1;
}
else if (c == '+') {
long b = stack.pop();
long a = stack.pop();
stack.push(a + b);
index = i + 1;
}
else if (c == '-') {
long b = stack.pop();
long a = stack.pop();
stack.push(a - b);
index = i + 1;
}
else if (c == '*') {
long b = stack.pop();
long a = stack.pop();
stack.push(a * b);
index = i + 1;
}
}
return stack.pop();
ps:请问大家有没有推荐的算法学习路线呀,比如优先学习什么算法啥的,刚开始接触算法,真挺茫然的现在T__T。