小妹使用栈实现计算器(下)
作者:互联网
我:好饱,怎么样,写出来了么?看你那愁眉哭脸,指定没写出来。
小妹:你很开心?
我:哈哈哈,也不是,就是吃饱了,舒坦。
小妹:找打呢你!
我:我来给你讲,别急别急。
小妹:你这脸变的可比翻书快啊。
我:额...抓紧时间,搞完你去吃饭多好。
思路给你了,哪里不明白。
小妹:带括号的,我不知道怎么实现。。。明明按照思路写的,就是不对。
我:带括号的,遇见左括号,你就直接入栈(这里也需要考虑括号内的运算符的优先级),遇见右括号,直接弹出s1的元素,将出栈的元素压入到s2,直到遇见左括号为止。直到分割的元素全部入栈,然后将s1的元素逐个出栈,按出栈的顺序压入s2中,然后输出的逆序即为逆波兰表达式。
(附上篇思路)
思路分析:
1.初始化两个栈;运算符栈s1和存储中间结果的栈s2
2.从左至右扫描中缀表达式
3.遇到操作符时,将其压入s2
4.遇到运算符时,比较其与s1栈顶运算符的优先级
1.如果s1为空,或栈顶运算符为左括号,则直接将此运算符入栈
2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
3.否则,将s1栈顶的运算符弹出并压入s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
5.遇到括号:
1.如果是左括号,直接压入s1
2.如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,
此时将这一对括号丢弃
6.重复步骤2-5,直到表达式的最右边
7.将s1中剩余的运算符依次弹出并压入s2
8.依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
现在你明白了吗?
小妹:这样啊。那我去实现一下,试一试。
public class CenterSwapSuffer {
Stack s1;
Stack s2;
public String swap(@NotNull String s) {
s1 = new Stack(s.length()+1);
s2 = new Stack(s.length()+1);
Stack split = this.split(s);
String[] c = new String[split.getLength() + 1];
int length = split.getLength() +1;
for (int i = length - 1; i >= 0; i--) {
c[i] = split.pop() + "";
}
for (String symbol : c) {
if (symbol.equals("(") || symbol.equals("+") || symbol.equals("-") || symbol.equals("*") || symbol.equals("/")) {
while (true) {
// 如果符号栈顶是左括号,或者为空直接让符号入栈
if (s1.peek() == null || symbol.equals("(") || s1.peek().equals("(")) {
s1.push(symbol);
break;
} else {
String peek = (String) s1.peek();
// s1栈顶符号优先权
int p1 = this.priority(peek);
// 当前符号优先权
int p2 = this.priority(symbol);
// 当前符号优先权大
if (p2 > p1) {
s1.push(symbol);
break;
} else {
// 弹出s1栈顶符号,并且压入s2中
String peekSymbol = (String) s1.pop();
s2.push(peekSymbol);
}
}
}
// 如果是右括号,就挨个弹出,再push进s2
} else if (")".equals(symbol)){
while (true) {
// 在遇见左括号后,将左括号弹出,并结束循环
if (s1.peek().equals("(")) {
s1.pop();
break;
}
String popS1 = (String)s1.pop();
s2.push(popS1);
}
} else {
s2.push(symbol);
}
}
while (s1.peek() != null) {
s2.push(s1.pop());
}
Object[] o = new Object[s2.getLength()+1];
for (int i = 0; i < o.length; i++) {
if (s2.peek() == null) break;
o[i] = s2.pop();
}
// 逆序输出返回
String s3 = "";
for (int i = o.length - 1; i >= 0 ; i--) {
s3 += o[i] + " ";
}
return s3;
}
/**
* 计算运算符的优先级
* @param s : 符号
* @return : 返回数字大的运算符优先级更高
*/
private int priority(@NotNull String s) {
int result = 0;
if ("+".equals(s) || "-".equals(s)) {
result = 1;
} else if ("*".equals(s) || "/".equals(s)) {
result = 2;
} else {
throw new RuntimeException("illegal symbol");
}
return result;
}
/**
* 分析字符串,将一个字符串转换为一个个的数字和运算符,并且入栈,方便计算
* @param s : 传入要计算的字符串
* @return : 返回一个分割后栈
*/
private Stack split(@NotNull String s) {
// 定义栈的大小,字符串分割之后存入栈中,最多不会超过字符串的最大长度。
// 所以初始化栈的大小为s.length()+1
Stack stack = new Stack(s.length() + 1);
int i = 0;
String str = "";
while (s.length() > i) {
// 挨个遍历字符串的内容
char c = s.charAt(i);
// 判断字符串分割出来的字符是否为数字(jdk的api)
if (Character.isDigit(c)) {
// 如果是数字,就拼接成String,不压入栈
str += c;
} else {
// 如果字符不是数字,证明一个数字已经拼接完成
// 在压入符号之前压入该数字,并且清空字符串,方便下次拼接
if (str != "") {
stack.push(str);
str = "";
}
// 符号入栈
stack.push(c);
}
i++;
}
// 遍历到最后,如果str不为空,则是最后一个数字,压入栈中即可
if (str != "") {
stack.push(str);
}
return stack;
}
}
你看这样写,对不对。
(字符串加入一个" " 空字符,是为了后面便于分割)
我:恩。。算是对了。不错不错,值得表扬。
小妹:拉到吧,我先去吃饭,回来再讲,给我讲一个计算器,你说了这么多,都没讲重点呢。
我:这都是重点,去吃饭,抓紧回来。
小妹:我的鸡蛋咋办?
我:下次一定,下次一定。
小妹:.....
...(30分钟后)
小妹:啊!好饱,舒坦。
我:既然吃饱了,继续吧。
小妹:想午休。
我:你在做梦,继续。
刚刚写完了表达式的转换,那么我们来讲讲怎么实现计算器吧。
小妹:啊,终于进入正题了。快说快说。
我:好,别着急,相反,这才是比较简单的步骤。刚刚的swap方法,不是返回了一个字符串吗?我们用jdk的split方法去分割一下这个字符串,这样
/**
* 分割逆波兰表达式
* @param str : 逆波兰表达式的串
* @return : 逆波兰表达式存放的List集合
*/
public List<String> splitString(String str) {
List<String> list = new ArrayList<>();
String[] split = str.split(" ");
for (String s : split) {
list.add(s);
}
return list;
}
然后再遍历这个字符串,利用栈进行求结果,如下:
public class SufferExpress {
private Stack stack = new Stack(10);
/**
* 分割逆波兰表达式
* @param str : 逆波兰表达式的串
* @return : 逆波兰表达式存放的List集合
*/
public List<String> splitString(String str) {
List<String> list = new ArrayList<>();
String[] split = str.split(" ");
for (String s : split) {
list.add(s);
}
return list;
}
public int cal(List<String> list) {
for (String s : list) {
// 如果s是数字,就进行入栈
if (s.matches("\\d+")) {
stack.push(Integer.parseInt(s));
} else {
int p1 = (Integer) stack.pop();
int p2 = (Integer)stack.pop();
int result = this.calVal(s, p1, p2);
stack.push(result);
}
}
System.out.println("result = " + stack.peek());
return (int)stack.peek();
}
// calculate value
private int calVal(String c, int p1, int p2) {
int res = 0;
if ("+".equals(c)) {
res = p1 + p2;
} else if ("-".equals(c)) {
res = p2 - p1;
} else if ("*".equals(c)) {
res = p2 * p1;
} else if ("/".equals(c)) {
res = p2 / p1;
} else {
throw new RuntimeException("符号有误!");
}
return res;
}
}
注意一点,由于栈是先进后出,所以在做减法和除法运算的时候,应该是后弹出的减去先弹出的,而加法和乘法没有区别。
你看这样写,简单吧。
小妹:让我研究一下,别着急。
(10分钟后)
(哇,太简单了,你太厉害了小哥哥。)
恩。。还不错,诶,诶,你发什么呆啊?
我:啊,额,你不夸我么?
小妹:夸你干嘛,这么简单,哈哈哈!
我:行吧行吧。这下懂了吧,别再用你那简单的代码拿出来丢人嗷!
小妹:滚!!!
我:好勒。
代码亲测,无错误,待优化,如有指点,万分感谢!
标签:String,实现,s2,s1,equals,运算符,int,小妹,计算器 来源: https://blog.csdn.net/weixin_44231805/article/details/118785567