C语言 | 计算器实现(中缀表示法/后缀表示法)
————————————————————————————————————————————
实现原理:
每个操作数都被依次压入栈中,当一个运算符到达时,从栈中弹出相应数目的操作数(对于二元运算符来说是两个操作数),把该运算符作用于弹出的操作数,并把运算结果再压入栈中
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
计算器(后缀表达式)
相关知识:
后缀表达式(逆波兰表示法):在逆波兰中,所有运算符都跟在操作数后面,如下:(1 - 2) *(4 + 5) 采用逆波兰表示法表示为:1 2 - 4 5 + *,不需要圆括号,只要知道每个运算符需要几个操作数就不会引起歧义
实现功能:
输入后缀表达式,以换行结束,计算四则运算结果。
对于 1 2 - 4 5 + * 来说,首先把1和2压入到栈中,再用两者之差-1取代它们;然后将4和5压入到栈中,再用两者之和9取代它们。最后从栈中取出栈顶的-1和9,并把它们的积-9压入到栈顶。到达输入行的末尾时,把栈顶的值弹出来并打印。
伪代码:
while 读入值不为换行时
if 是数字
压入栈中
else if 是运算符
弹出两个运算数,计算并压栈
else 输入错误并退出
end if
读入值
弹出最终结果并打印
实现代码:
1 /* 实现功能:输入后缀表达式,以换行结束,计算四则运算结果 */ 2 /* 这种后缀表示法只需要一个栈就可以了,遇到符号则弹运算数,但是中缀就不一样 */ 3 #include <stdio.h> 4 #include <stdlib.h> 5 #define OK 1 6 #define ERROR 0 7 #define OVERFLOW -2 8 #define STACK_INIT_SIZE 100 9 #define STACKINCREAMENT 10 10 typedef int Status; 11 typedef char SElemType; 12 typedef struct 13 { 14 SElemType *top; 15 SElemType *base; 16 int stacksize; 17 } SqStack; 18 Status InitStack(SqStack *s) 19 { 20 s->base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType)); 21 if (!s->base) exit(OVERFLOW); 22 s->top = s->base; 23 s->stacksize = STACK_INIT_SIZE; 24 return OK; 25 } 26 Status Push(SqStack *s, SElemType e) 27 { 28 if (s->top - s->base == s->stacksize) 29 { 30 s->base = (SElemType *)realloc(s->base, (s->stacksize + STACKINCREAMENT) * sizeof(SElemType)); 31 if (!s->base) exit(OVERFLOW); 32 s->top = s->base + s->stacksize; 33 s->stacksize += STACKINCREAMENT; 34 } 35 s->top++; 36 *(s->top) = e; 37 return OK; 38 } 39 Status Pop(SqStack *s, SElemType *e) 40 { 41 if (s->top == s->base) exit(OVERFLOW); 42 *e = *(s->top); 43 s->top--; 44 return OK; 45 } 46 Status Empty(SqStack s) 47 { 48 if (s.top - s.base == 0) 49 return OK; 50 else 51 return ERROR; 52 } 53 int main() 54 { 55 SqStack OPND; //OPTR是运算符 OPND是运算数 56 char c, num1, num2; 57 InitStack(&OPND); 58 while((c = getchar()) != '\n') 59 { 60 switch(c) 61 { 62 case '0': 63 case '1': 64 case '2': 65 case '3': 66 case '4': 67 case '5': 68 case '6': 69 case '7': 70 case '8': 71 case '9': 72 Push(&OPND, c - '0'); 73 break; 74 case '+': 75 Pop(&OPND, &num2); 76 Pop(&OPND, &num1); 77 Push(&OPND, num1 + num2); 78 break; 79 case '-': 80 Pop(&OPND, &num2); 81 Pop(&OPND, &num1); 82 Push(&OPND, num1 - num2); 83 break; 84 case '*': 85 Pop(&OPND, &num2); 86 Pop(&OPND, &num1); 87 Push(&OPND, num1 * num2); 88 break; 89 case '/': 90 Pop(&OPND, &num2); 91 Pop(&OPND, &num1); 92 Push(&OPND, num1 / num2); 93 break; 94 default: 95 break; 96 } 97 } 98 while(!Empty(OPND)) 99 { 100 Pop(&OPND, &c); 101 printf("%d ", c); 102 } 103 return OK; 104 }
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
计算器(中缀表示法)
运算符优先级:
+ |
- |
* |
/ |
( |
) |
# |
|
+ |
> |
> |
< |
< |
< |
> |
> |
- |
> |
> |
< |
< |
< |
> |
> |
* |
> |
> |
> |
> |
< |
> |
> |
/ |
> |
> |
> |
> |
< |
> |
> |
( |
< |
< |
< |
< |
< |
= |
0 |
) |
> |
> |
> |
> |
0 |
> |
> |
# |
< |
< |
< |
< |
< |
0 |
= |
执行过程:
在计算 #4+3*(5-10)/5# 时栈中的执行过程如下
步骤 |
输入字符 |
执行操作 |
OPTR |
OPND |
1 |
# |
Push(#) |
# |
|
2 |
4 |
Push(4) |
# |
4 |
3 |
+ |
'#'<'+',Push(+) |
# + |
4 |
4 |
3 |
Push(3) |
# + |
4 3 |
5 |
* |
'+'<'*',Push(*) |
# + * |
4 3 |
6 |
( |
'*'<'(',Push(() |
# + * ( |
4 3 |
7 |
5 |
Push(5) |
# + * ( |
4 3 5 |
8 |
- |
'('<'-',Push(-) |
# + * ( - |
4 3 5 |
9 |
10 |
Push(10) |
# + * ( - |
4 3 5 10 |
10 |
) |
'-'>')',计算并压入结果 |
# + * ( |
4 3 -5 |
11 |
|
'('=')',脱括号 |
# + * |
4 3 -5 |
12 |
/ |
'*'>'/'计算并压入结果 |
# + |
4 -15 |
13 |
|
'+'<'/',Push(/) |
# + / |
4 -15 |
14 |
5 |
Push(5) |
# + / |
4 -15 5 |
15 |
# |
'/'>'#',计算并压入结果 |
# + |
4 -3 |
16 |
|
'+'>'#',计算并压入结果 |
# |
1 |
17 |
|
'#'='#',脱括号 |
|
1 |
伪代码:
初始化运算符栈;压入#;
初始化运算数栈;获取输入;
while 获取输入不为#或栈顶不为#
if 输入的是数字
压入运算数栈
获取输入
if 新输入的也是数字
十位数百位数运算
end if
else
switch 运算符栈顶与当前输入优先级比较
压入运算符栈
获得输入
弹出运算符栈顶(或#
获得输入
弹出运算符栈顶
弹出两个运算数
计算并将结果压入运算数栈
//此时不获取新输入,该循环输入的运算符作为c重新进入循环
end if
end while
输出运算数栈中剩的运算数
实现代码:
1 /* 只能运算-128~127之间的结果 */ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <math.h> 5 #define OK 1 6 #define ERROR 0 7 #define SPILL -2 //math.h中已有OVERFLOW,则改用SPILL 8 #define STACK_INIT_SIZE 100 9 #define STACKINCREAMENT 10 10 typedef char SElemType; 11 typedef int Status; 12 typedef struct 13 { 14 SElemType *top; 15 SElemType *base; 16 int stacksize; 17 } SqStack; 18 Status InitStack(SqStack *s) 19 { 20 s->base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType)); 21 if (!s->base) 22 exit(SPILL); 23 s->top = s->base; 24 s->stacksize = STACK_INIT_SIZE; 25 return OK; 26 } 27 Status EmptyStack(SqStack s) 28 { 29 if (s.top - s.base == 0) 30 return OK; 31 else 32 return ERROR; 33 } 34 Status Push(SqStack *s, SElemType e) 35 { 36 if (s->top - s->base == s->stacksize) 37 { 38 s->base = (SElemType *)realloc(s->base, (s->stacksize + STACKINCREAMENT) * sizeof(SElemType)); 39 if (!s->base) 40 exit(SPILL); 41 s->top = s->base + s->stacksize; 42 s->stacksize += STACKINCREAMENT; 43 } 44 s->top ++; 45 *(s->top) = e; 46 return OK; 47 } 48 Status Pop(SqStack *s, SElemType *e) 49 { 50 if (s->top == s->base) 51 exit(SPILL); 52 *e = *(s->top); 53 s->top--; 54 return OK; 55 } 56 SElemType GetTop(SqStack s) 57 { 58 return *(s.top); 59 // *e = *(s.top); 60 // return OK; 61 } 62 /* 判断如果是数字则返回OK,运算符返回ERROR,非法输入则退出 */ 63 Status InputJudge(SElemType c) 64 { 65 switch(c) 66 { 67 case '0': 68 case '1': 69 case '2': 70 case '3': 71 case '4': 72 case '5': 73 case '6': 74 case '7': 75 case '8': 76 case '9': 77 return OK; 78 break; 79 case '+': 80 case '-': 81 case '*': 82 case '/': 83 case '(': 84 case ')': 85 case '#': 86 return ERROR; 87 break; 88 default: 89 exit(SPILL); 90 break; 91 } 92 } 93 /* 当前输入的运算符和前一个运算符比较优先级 */ 94 SElemType PriorityJudge(SElemType optr1, SElemType optr2) 95 { 96 int i, j; 97 char priorityTable[7][7] = 98 { 99 {'>', '>', '<', '<', '<', '>', '>'}, 100 {'>', '>', '<', '<', '<', '>', '>'}, 101 {'>', '>', '>', '>', '<', '>', '>'}, 102 {'>', '>', '>', '>', '<', '>', '>'}, 103 {'<', '<', '<', '<', '<', '=', '0'}, 104 {'>', '>', '>', '>', '0', '>', '>'}, 105 {'<', '<', '<', '<', '<', '0', '='} 106 }; 107 switch(optr1) 108 { 109 case '+': 110 i = 0; 111 break; 112 case '-': 113 i = 1; 114 break; 115 case '*': 116 i = 2; 117 break; 118 case '/': 119 i = 3; 120 break; 121 case '(': 122 i = 4; 123 break; 124 case ')': 125 i = 5; 126 break; 127 case '#': 128 i = 6; 129 break; 130 } 131 switch(optr2) 132 { 133 case '+': 134 j = 0; 135 break; 136 case '-': 137 j = 1; 138 break; 139 case '*': 140 j = 2; 141 break; 142 case '/': 143 j = 3; 144 break; 145 case '(': 146 j = 4; 147 break; 148 case ')': 149 j = 5; 150 break; 151 case '#': 152 j = 6; 153 break; 154 } 155 return priorityTable[i][j]; 156 } 157 /* 四则运算 */ 158 SElemType Calc(SElemType optr, SElemType num1, SElemType num2) 159 { 160 switch(optr) 161 { 162 case '+': 163 return (num1 + num2); 164 break; 165 case '-': 166 return (num1 - num2); 167 break; 168 case '*': 169 return (num1 * num2); 170 break; 171 case '/': 172 return (num1 / num2); 173 break; 174 } 175 } 176 int main() 177 { 178 char c, optr, num1, num2, temp; 179 SqStack OPND, OPTR; 180 InitStack(&OPTR); 181 Push(&OPTR, '#'); 182 InitStack(&OPND); 183 c = getchar(); 184 while(c != '#' || GetTop(OPTR) != '#') 185 // while(!EmptyStack(OPTR)) 186 //严蔚敏老师书上的算法是判断输入非#或栈顶非#时循环,个人认为判断运算符栈不为空也可以,当初始化时压入的#闭合,结束运算 187 { 188 if (InputJudge(c)) 189 { 190 Push(&OPND, c - '0'); 191 c = getchar(); 192 /* 当连续输入数字时,计算十位数和百位数 */ 193 while(InputJudge(c)) 194 { 195 int i = 1; 196 Pop(&OPND, &temp); 197 Push(&OPND, temp * pow(10, i) + (c - '0')); 198 i++; 199 c = getchar(); 200 } 201 } 202 else 203 switch(PriorityJudge(GetTop(OPTR), c)) 204 { 205 case '<': 206 Push(&OPTR, c); 207 c = getchar(); 208 break; 209 case '=': 210 Pop(&OPTR, &c); 211 c = getchar(); 212 break; 213 case '>': 214 Pop(&OPTR, &optr); 215 Pop(&OPND, &num2); 216 Pop(&OPND, &num1); 217 Push(&OPND, Calc(optr, num1, num2)); 218 break; 219 } 220 } 221 while(!EmptyStack(OPND)) 222 { 223 Pop(&OPND, &c); 224 printf("%d\n", c); 225 } 226 return OK; 227 }