之计算中缀表达式
1.中缀表达式转后缀表达式
1.1中缀表达式和后缀表达式(反波兰表达式)
我们日常生活中使用的表达式就是中缀表达式,即将运算符放在两个操作数之间进行运算
如:2+4x(6-5)、5x(3+4/2)-2。
但是因为界限符的存在,利用计算机不方便直接计算,所以引入后缀表达式,就是将运算符放在两个操作数之后进行运算。那么上式中缀表达式转变为
2465-x+、5342/+x2-。
以第一个为例:
首先计算操作数6,5进行-运算,得到1,此时表达式为:241x+;
然后计算操作数4,1进行x运算,得到4,此时表达式为:24+;
最后计算操作数2,4进行+操作,得到结果6。
1.2计算的左优先原则
中缀表达式转后缀表达式手算会转出不同的结果:
比如A+Bx(C-D)-E/F
可以转为ABCD-xEF/-+,先计算Bx(C-D) -E/F,最后加上A
也可以转为ABCD-x+EF/-,先计算A+Bx(C-D),最后减去E/F
但算法的确定性要求一种输入只能得到唯一的输出,所以机算得到的结果就要坚持左优先原则,在计算优先级相同的情况下,先来后到,靠左的运算符先加入运算。那么上式的转化中,ABCD-x+EF/- 是正确的.
1.3算法思想
将中缀表达式以字符串形式输入,从左到右遍历每一个字符
- 遇到数字,则直接加入到后缀表达式
- 遇到”(“, 压入运算符栈
- 遇到”)“,依次弹出栈内运算符加入后缀表达式,直到遇到"(",然后将”(“弹出
- 遇到运算符号,依次弹出优先级高于或者**等于(因为左优先原则,先入栈的运算符肯定是更靠左边的)**当前运算符的运算符加入后缀表达式,直到遇到”(“(记得将”(“弹出)或者栈空为止,然后当前运算符入栈
遍历完字符,将栈中剩余运算符依次弹出。
以A+Bx(C-D)-E/F为例
遍历到的字符 | 后缀表达式 | 运算符栈(右边为栈顶) |
---|---|---|
A | A | NULL |
+ | A | + |
B | AB | + |
x | AB | +x |
( | AB | +x( |
C | ABC | +x( |
- | ABC | +x(- |
D | ABCD | +x(- |
) | ABCD- | +x |
- | ABCD-x+ | - |
E | ABCD-x+E | - |
/ | ABCD-x+E | -/ |
F | ABCD-x+EF | -/ |
遍历结束,依次弹出运算符 | ABCD-x+EF/- | NULL |
1.4代码片
骗你的,这段没有代码,写的应该没问题,但是后缀表达式转不出来,躁死我了
2.计算后缀表达式
2.1算法思想
将后缀表达式以字符串形式输入,从左到右依次遍历字符
- 遇到数字,压入操作数栈
- 遇到运算符,依次弹出两个操作数n2和n1(注意,先弹出来的的是操作数2,如果不注意的话除法会出错,
因为我就错了,找半天错属于是),进行当前运算符的计算,将结果再次压入到栈内
遍历完字符,最终结果就在栈顶,自取即可。
以**ABCD-x+EF/-**为例
遍历到的字符 | n1 | n2 | 操作数栈 (右边为栈顶,每个元素用空格隔开) |
---|---|---|---|
A | A | ||
B | A B | ||
C | A B C | ||
D | A B C D | ||
- | C | D | A B C-D |
x | B | C-D | A B*(C-D) |
+ | A | B*(C-D) | A+B*(C-D) |
E | A+B*(C-D) E | ||
F | A+B*(C-D) E F | ||
/ | E | F | A+B*(C-D) E/F |
- | A+B*(C-D) | E/F | A+B*(C-D)-E/F |
2.2代码片
#include<iostream>
#define MaxSize 20
typedef struct Stack{
int data[MaxSize];
int top;
}Stack;
bool InitStack(Stack &s){
if(s.top == -1)
return false;
s.top = -1;
for(int i = 0; i < MaxSize; i++)
s.data[i] = 0;
return true;
}
bool IsEmpty(Stack s){
if(s.top == -1)
return true;
else
return false;
}
bool Push(Stack &s, int x){
if(s.top == MaxSize - 1)
return false;
s.data[++s.top] = x;
return true;
}
bool Pop(Stack &s, int &x){
if(s.top == -1)
return false;
x = s.data[s.top--];
return true;
}
bool PolishNotation(char str[], int &result){
Stack s;
int i = 0, OpNum1, OpNum2;
InitStack(s);
while(str[i] != '\0'){
if(str[i] >= 48 && str[i] <= 57)
Push(s, (int)(str[i]-48));
else if(str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' || str[i] == 'x'){
if(!Pop(s, OpNum2)){
std::cout << "缺少操作数导致与运算符号不匹配" << std::endl;
return false;
}
if(!Pop(s, OpNum1)){
std::cout << "缺少操作数导致与运算符号不匹配" << std::endl;
return false;
}
if(str[i] == '+')
Push(s, OpNum2 + OpNum1);
else if(str[i] == '-')
Push(s, OpNum2 - OpNum1);
else if(str[i] == '*' || str[i] == 'x')
Push(s, OpNum2 * OpNum1);
else
Push(s, OpNum2 / OpNum1);
}
else{
std::cout << "非法字符" << std::endl;
return false;
}
i++;
}
Pop(s, result);
if(IsEmpty(s)){
return true;
}
else{
std::cout << "缺少运算符号" << std::endl;
return false;
}
}
int main(){
char Notation[20];
int result;
std::cout << "输入后缀表达式" << std::endl;
std::cin >> Notation;
if(PolishNotation(Notation, result))
std::cout << result << std::endl;
return 0;
}
3.中缀表达式的计算(就是中缀转后缀的同时计算后缀)
3.1算法思想
创建两个栈,操作数栈和运算符栈
将中缀表达式以字符串形式输入,从左到右遍历每一个字符
- 遇到数字,压入操作数栈内
- 遇到”(“,将”(“压入运算符栈
- 遇到”)“,运算符栈弹出一个运算符,操作数栈弹出操作数n2和n1,执行n1 运算 n2,循环此操作直到栈顶元素是”(“,然后将”(“弹出栈
- 遇到运算符,弹出运算符栈内一个运算符,操作数栈弹出操作数n2和n1,执行n1 运算 n2,循环此操作直到栈顶元素是”(“ 或者 空栈 或者 运算优先级低于当前运算符,再将当前运算符压入栈内
遍历完字符,弹出运算符栈内一个运算符,操作数栈弹出操作数n2和n1,执行n1 运算 n2,直到运算符栈为空。
3.2代码片
此代码在把中缀表达式中表示数字的字符串转成数字的部分尚有缺陷,还不能输入小数进行计算,而且算法有待改进… …
#include<iostream>
#include<stdlib.h>
typedef struct LNode{
char O;
float N;
struct LNode *next;
}LNode, *Stack;
bool InitStack(Stack &s){
s = NULL;
return true;
}
bool IsEmpty(Stack s){
return (s == NULL);
}
bool PushOp(Stack &s, char c){
LNode *p = (LNode*)malloc(sizeof(LNode));
if(p == NULL)
return false;
p->O = c;
p->next = s;
s = p;
return true;
}
bool PopOp(Stack &s, char &c){
if(IsEmpty(s))
return false;
LNode *p = s;
c = p->O;
s = s->next;;
free(p);
return true;
}
bool PushNum(Stack &s, float x){
LNode *p = (LNode*)malloc(sizeof(LNode));
if(p == NULL)
return false;
p->N = x;
p->next = s;
s = p;
return true;
}
bool PopNum(Stack &s, float &x){
if(IsEmpty(s))
return false;
LNode *p = s;
x = p->N;
s = s->next;;
free(p);
return true;
}
bool FreeStack(Stack &s){
if(s == NULL)
return true;
LNode *p;
while(s != NULL){
p = s;
s = s->next;
free(p);
}
return true;
}
float Cal(float n1, float n2, char o){ //计算加减乘除
if(o == '+')
return (n1 + n2);
else if(o == '-')
return (n1 - n2);
else if(o == '*' || o == 'x')
return (n1 * n2);
else if(o == '/' || o == '÷')
return (n1 / n2);
}
bool CalResult(Stack &num, Stack &op, float &n1, float&n2, char &o){
PopOp(op, o); //运算符弹出到字符o上
if(!PopNum(num, n2)){ //从操作数栈弹出一个操作数n2 (先弹出栈的是操作数2)
std::cout << "表达式有误" << o << "后面缺少操作数字" << std::endl;
FreeStack(op);
FreeStack(num);
return false;
}
if(!PopNum(num, n1)){ //从操作数栈弹出一个操作数n1
std::cout << "表达式有误" << o << "前面缺少操作数字" << std::endl;
FreeStack(op);
FreeStack(num);
return false;
}
return true;
}
bool NotationResult(char str[], float &result){
Stack op, num;
int i = 0;
float n1, n2;
char o;
InitStack(op);
InitStack(num);
while(str[i] != '\0'){
if(str[i] >= 48 && str[i] <= 57){
int nowposition = i;
n1 = 0;
for(;str[i] >= 48 && str[i] <= 57; i++){
PushNum(num, (int)(str[i] - 48));
}
int place = i - nowposition, a = 1; //得到位数
for(int j = 1; j <= place; j++){
float x;
PopNum(num, x);
n1 = n1 + x * a;
a = a * 10;
}
i-=1; //while循环里i还会加一次,所以要减一
PushNum(num, n1);
}
else if(str[i] == '('){
PushOp(op, str[i]);
}
else if(str[i] == '+' || str[i] == '-'){
while(!IsEmpty(op) && op->O != '('){ //左优先原则,只要运算符栈内不碰到括号和栈空,就弹出栈内运算符进行计算
if(CalResult(num, op, n1, n2, o))
PushNum(num, Cal(n1, n2, o)); //计算结果压入操作数栈
else
return false;
}
PushOp(op, str[i]);
}
else if(str[i] == '*' || str[i] == '/' || str[i] == 'x' || str[i] == '÷'){
while(op->O == '*' || op->O == '/'|| op->O == 'x' || op->O == '÷' && !IsEmpty(op)){
if(CalResult(num, op, n1, n2, o)){
if(str[i] == '/' || str[i] == '÷'){
if(n2 == 0){
std::cout << "表达式有误 被除数不能为零" << std::endl;
FreeStack(op);
FreeStack(num);
return false;
}
}
PushNum(num, Cal(n1, n2, o)); //计算结果压入操作数栈
}
else
return false;
}
PushOp(op, str[i]);
}
else if(str[i] == ')'){
while(op->O != '('){
if(CalResult(num, op, n1, n2, o))
PushNum(num, Cal(n1, n2, o)); //计算结果压入操作数栈
else
return false;
}
PopOp(op, o); //将'('弹出
}
else{
std::cout << "非法字符" << str[i] << std::endl;
FreeStack(op);
FreeStack(num);
return false;
}
i++;
}
while(!IsEmpty(op)){
if(CalResult(num, op, n1, n2, o))
PushNum(num, Cal(n1, n2, o)); //计算结果压入操作数栈
else
return false;
}
PopNum(num, result);
if(!IsEmpty(num)){
std::cout << "表达式有误 操作数多余" << std::endl;
FreeStack(op);
FreeStack(num);
return false;
}
return true;
}
int main(){
char Notation[20];
float result = 0;
std::cout << "输入中缀表达式" << std::endl;
std::cin >> Notation;
if(NotationResult(Notation, result))
std::cout << result << std::endl;
return 0;
}