摘要:我来实验室两个星期了。 用强壮的啤酒来说,基本上是自拍状态。 勤奋的柠檬听说你向c学习了吗? 你能帮我做一个简单的计算器吗? 我想这台计算机一定没有只计算加减乘除那么简单。 因此仔细想想,为了写能使用括号的计算机,最好能写简单的UI。 考虑中发现,该计算机的难点在于如何将骑马订表达式转换为后缀表达式,以及如何计算后缀表达式。
计算的想法
人的想法
如果只是为了解决问题的话,这个方法最快也是正确的。 但不适用于计算机。 以下以AB*C(D*EF ) g为例,说明以下人员应该如何将骑马订格式转换为后缀格式:
按照先加减后乘除的原则在公式上加括号
结果: ((a ) b*c ) ) ) ) ) d*e ) f ) *g ) )
用后缀从内到外替换每个括号中的表达式
最终结果: abc* de*f g*
这将得到骑马订表达式转换为后缀表达式的最终结果。 该方法对应对试验有效。
计算机思维方式
毕竟电脑和别人不一样,是“笨蛋”啊。 别人的想法那样不能用。 那么,该如何将骑马订表达式转换为后缀表达式呢?
我的想法需要堆栈。 首先,明确从骑马订式到后缀式的转换规则。
1 )遇到操作数时,我们直接输出它。
2 )遇到操作员时放入堆栈,遇到左括号时也放入堆栈。
3 )如果检测到右括号,则弹出堆栈元素,并输出弹出的操作符直到检测到右括号。 请注意,左括号仅突出显示不会输出。
4 )遇到其他操作符(例如,()、()、()、()、()、()、()、()等)时,从堆栈弹出元素直到找到优先级更低的元素)或堆栈为空。 弹出这些元素后,将遇到的操作员推入堆栈。 有需要注意的事情。 只有在遇到“”“”时,我们才会弹出“”“”。 否则,不弹出“”“”。
5 )阅读输入末尾后,依次弹出堆栈中的所有元素。
以aB*c(d*ef ) g为例说明计算机的转换过程。 以下说明堆栈时,直接用文字记述,从左到右从堆栈底部到堆栈顶部。 天空表示堆栈天空
从左向右遍历公式,首先遇到a,然后直接输出。
此时的输出为a
对于堆栈,它为空
继续遍历,相遇,放入堆栈。
此时的输出为a
对于堆栈:
继续遍历,遇到b,直接输出它。
此时的输出为ab
对于堆栈:
继续遍历,遇到*。 *的优先级大于堆栈顶部,因此将*放入堆栈中。
此时的输出为ab
堆栈状况为*
继续遍历,遇到c,直接输出它。
此时的输出为abc
堆栈状况为*
继续遍历,遇到,的优先级低于堆栈顶部的*,弹出*; 而且,由于新堆栈顶的要素的优先顺序与该优先顺序相同,所以当前的堆栈顶的也必须弹出; 然后堆栈空了,现在把这个放进堆栈里。
此时的输出为abc*
对于堆栈:
继续遍历,(,直接放入堆栈,不遇到),即使遇到)不弹。
此时的输出为abc*
对于堆栈,请使用(
继续遍历,遇到d,直接输出它。
此时的输出为abc* d
对于堆栈,请使用(
继续遍历,遇到*。 栈顶为(,不遇到) ),因此将)直接放入栈中。
此时的输出为abc* d
对于堆栈,请单击(*
继续遍历,遇到e,直接输出它。
此时的输出为abc* de
对于堆栈,请单击(*
继续遍历,遇到,优先级低于堆栈顶部*,弹出*; 新堆栈的最上面的元素是(,不遇到),不出去),因此将其放入堆栈中。
此时的输出为abc* de*
对于堆栈,请使用(
继续遍历,遇到f,直接输出它。
此时的输出为abc* de*f
对于堆栈,请使用(
遍历,遇到),然后直接按顺序弹出堆栈中的元素,直到遇到()为止。 )。 )弹出,但不输出。
此时的输出为abc* de*f
对于堆栈:
继续遍历,遇到*。 *的优先级大于堆栈顶部元素的优先级,因此直接将*放入堆栈中。
此时的输出为abc* de*f
堆栈状况为*
继续遍历,遇到g,直接输出。
此时的输出为abc* de*f g
堆栈状况为*
继续遍历,清空,结束遍历。 按顺序弹出堆栈中的元素。
此时的输出为abc* de*f g*
对于堆栈,它为空
现在,所有骑马订后缀都已完成,结果为abc* de*f g*。
代码实现
源代码
代码是用c写的,但还是用了面向过程的思路。 代码如下。
//骑马订式后缀
#包含
#包含
#包含
用户命名空间STD;
p>int prio(char op) { //给运算符优先级排序
int priority;
if (op == '*' || op == '/')
priority = 2;
if (op == '+' || op == '-')
priority = 1;
if (op == '(')
priority = 0;
return priority;
}
bool Trans(string &str,string &str1) { //引用传递
stack s; //定义一个char类型的栈s
int i;
for (i = 0; i
if (str[i] >= '0' && str[i] <= '9') { //如果是数字,直接入栈
str1+=str[i];
}
else { //否则不是数字
if (s.empty()) //栈空则入站
s.push(str[i]);
else if (str[i] == '(') //左括号入栈
s.push(str[i]);
else if (str[i] == ')') { //如果是右括号,只要栈顶不是左括号,就弹出并输出
while (s.top() != '(') {
str1+= s.top();
s.pop();
}
s.pop(); //弹出左括号,但不输出
}
else {
while (prio(str[i]) <= prio(s.top())) { //栈顶优先级大于等于当前运算符,则输出
str1+= s.top();
s.pop();
if (s.empty()) //栈为空,停止
break;
}
s.push(str[i]); //把当前运算符入栈
}
}
}
while (!s.empty()) { //最后,如果栈不空,则弹出所有元素并输出
str1+= s.top();
s.pop();
}
return true;
}
int main() { //主程序
string infix;
string postfix;
cout << "请输入中缀表达式:" << infix << endl;
cin >> infix;
Trans(infix,postfix);
cout << "后缀表达式为:" << postfix << endl;
return 1;
}
程序测试
这里我们就用a+b*c+(d*e+f)*g的实例1+2*3+(4*5+6)*7来测试我们的程序,可见运行结果为123*+45*6+7*+,计算正确。
intopost.png
总结
在写这段小程序时有一些不小的收获。毕竟是一个初学者,犯的错误都是很低级的知识性错误,理解不到位,基础不扎实。这里要感谢我的本科同学白洋耐心地教我。
1)string类的对象在没有初始化大小的情况下不要用其下标运算,会出现string subscript out of range(下标越界)的错误。在第三次执行循环时出现,大概是因为string对象的默认大小为2个字节。
2)string类的对象不以' '结尾,要与C语言区分开来,判断其结束时用size()函数。size() 函数返回的是其大小,然后下标是以0开始的,所以最大到size-1。
3)函数调用进行形实结合时要用引用传递。
4)#include的使用。