本文是对严蔚敏老师的数据结构一书中,第3.2.5一节 表达式求值问题的实现
采用的基本思想是“算符优先法”,即:
(1)先乘除,后加减;
(2)从左到右计算;
(3)先括号内,后括号外
实现的方式:采用两个工作栈,一个寄存运算符;一个寄存运算数;
(1)首先置操作数栈为空,置操作符栈'='作为栈底元素;
(2)依次分析表达式的每个字符,如果是操作数则进操作数栈,若是操作符,则和操作符栈的栈顶元素比较优先级后作出相应操作,直到整个表达式求值完毕(即字符串分析完毕或操作符栈的栈顶元素为'=')
运算符优先级采用二维数据保存,优先关系如下图:
源码如下:
CalcExp.c
1 /********************************************************************* 2 *: 3 *: Author: dspeeding 4 *: Copyright (c) 2012, dspeeding 5 *: 6 *: Created at: 2013.08.07 7 *: Last modified: 2013.08.07 8 *: 9 *: Introduction: 表达式求值(栈实现) 10 *: 11 *: 12 *:*********************************************************************/ 13 #include <stdio.h> 14 #include <string.h> 15 #include "LinkListStack.h" 16 17 18 LinkListStack gStack_opt; /*操作符*/ 19 LinkListStack gStack_opnum; /*操作数*/ 20 21 void Trim(char* str) 22 { 23 char szTmp[1025]; 24 memset(szTmp,0,sizeof(szTmp)); 25 char *p = NULL; 26 char *q = NULL; 27 int i,nLen; 28 29 p = str; 30 q = szTmp; 31 32 nLen = strlen(str); 33 for(i = 0;i < nLen;i++) 34 { 35 if(str[i] != ' ') 36 { 37 *q++ = str[i]; 38 } 39 } 40 41 //如果输入没有=号则程序自动加入该等号 42 if(*(q-1)!='=') 43 { 44 *q++='='; 45 } 46 *q='\0'; 47 48 strcpy(str,szTmp); 49 } 50 51 52 53 char *getLine(){ 54 static char str[1025]; 55 fgets(str, 1024, stdin); 56 int len = strlen(str); 57 while(--len >= 0){ 58 if(str[len]=='\r' || str[len]=='\n') 59 str[len] = '\0'; 60 else 61 break; 62 } 63 return str; 64 } 65 66 67 /*检查表达式中有无违法字符 */ 68 int CheckString(const char* str) 69 { 70 int i,nLen; 71 nLen = strlen(str); 72 73 for(i = 0;i < nLen;i++) 74 { 75 if(str[i]<'0'&&str[i]>'9'&&str[i]!='('&&str[i]!=')' 76 &&str[i]!='*'&&str[i]!='+'&&str[i]!='-'&&str[i]!='/' 77 &&str[i]!='=') 78 { 79 return 1; 80 } 81 } 82 return 0; 83 } 84 85 //比较2个操作符的优先级 86 char precede(char x,char y) 87 { 88 int i,j; 89 int form[7][7]={ 90 { 1, 1,-1,-1,-1, 1, 1}, 91 { 1, 1,-1,-1,-1, 1, 1}, 92 { 1, 1, 1, 1,-1, 1, 1}, 93 { 1, 1, 1, 1,-1, 1, 1}, 94 {-1,-1,-1,-1,-1, 0, 2}, 95 { 1, 1, 1, 1, 2, 1, 1}, 96 {-1,-1,-1,-1,-1, 2, 0}}; 97 switch(x) 98 { 99 case '+':i=0;break; 100 case '-':i=1;break; 101 case '*':i=2;break; 102 case '/':i=3;break; 103 case '(':i=4;break; 104 case ')':i=5;break; 105 case '=':i=6;break; 106 } 107 switch(y){ 108 case '+':j=0;break; 109 case '-':j=1;break; 110 case '*':j=2;break; 111 case '/':j=3;break; 112 case '(':j=4;break; 113 case ')':j=5;break; 114 case '=':j=6;break; 115 } 116 117 if(form[i][j] == 1) 118 { 119 return '>'; 120 } 121 else if(form[i][j] == -1) 122 { 123 return '<'; 124 } 125 else 126 { 127 return '='; 128 } 129 } 130 131 132 int calc(int num1,int num2,char opt) 133 { 134 switch(opt) 135 { 136 case '+': 137 return num1 + num2; 138 case '-': 139 return num1 - num2; 140 case '*': 141 return num1 * num2; 142 case '/': 143 if(num2 == 0) 144 { 145 return 0; 146 } 147 return num1 / num2; 148 } 149 } 150 151 int AnalyString(char* str) 152 { 153 char opt = '='; 154 char szTmp[1024]; 155 int num1,num2,num_tmp; 156 char *p = NULL; 157 char *q = NULL; 158 char byRet; 159 ElemType outData; 160 int num_result; 161 162 163 memset(szTmp,0,sizeof(szTmp)); 164 //首先把'='入栈 165 PushLinkListStack(&gStack_opt, (PElemType)&opt); 166 p = str; 167 q = szTmp; 168 GetTopLinkListStack(&gStack_opt, &outData); 169 while(*p!='\0' || outData != '=') 170 { 171 if(*p>='0' && *p<='9') 172 { 173 //操作数 174 *q++ = *p++; 175 } 176 else 177 { 178 //操作符 179 if(strlen(szTmp)!=0) 180 { 181 num_tmp = atoi(szTmp); 182 memset(szTmp,0,sizeof(szTmp)); 183 q = szTmp; 184 185 //操作数入栈 186 printf("操作数[%d]入栈\n",num_tmp); 187 PushLinkListStack(&gStack_opnum, (PElemType)&num_tmp); 188 } 189 opt = *p; 190 GetTopLinkListStack(&gStack_opt, &outData); 191 byRet = precede((char)outData, opt); 192 switch(byRet) 193 { 194 case '<': 195 printf("操作符[%c]入栈\n",opt); 196 PushLinkListStack(&gStack_opt, (PElemType)&opt); 197 p++; //字符串指针后移 198 break; 199 case '>': 200 //在>时 进行运算完毕后p不进行++操作 201 PopLinkListStack(&gStack_opnum, &num2); 202 printf("操作数[%d]出栈\n",num2); 203 PopLinkListStack(&gStack_opnum, &num1); 204 printf("操作数[%d]出栈\n",num1); 205 PopLinkListStack(&gStack_opt, &outData); 206 printf("操作符[%c]出栈\n",(char)outData); 207 num_result = calc(num1, num2, (char)outData); 208 PushLinkListStack(&gStack_opnum, (PElemType)&num_result); 209 printf("操作数[%d]入栈\n",num_result); 210 break; 211 case '=': 212 PopLinkListStack(&gStack_opt, &outData); 213 printf("操作符[%c]出栈\n",(char)outData); 214 p++; //字符串指针后移 215 break; 216 } 217 } 218 GetTopLinkListStack(&gStack_opt, &outData); 219 } 220 return 0; 221 } 222 223 int main() 224 { 225 int outData; 226 InitLinkListStack(&gStack_opt); 227 InitLinkListStack(&gStack_opnum); 228 229 char *input = getLine(); 230 Trim(input); 231 if(CheckString(input)) 232 { 233 printf("输入表达式错误\n"); 234 return 1; 235 } 236 printf("输入数据[%s]\n", input); 237 238 if(AnalyString(input)) 239 { 240 printf("解析表达式错误\n"); 241 return 1; 242 } 243 if(IsEmptyLinkListStack(&gStack_opt) != 0) 244 { 245 printf("表达式错误\n"); 246 return -1; 247 } 248 printf("表达式结果:\t"); 249 if(IsEmptyLinkListStack(&gStack_opnum)) 250 { 251 if(PopLinkListStack(&gStack_opnum, &outData)) 252 { 253 printf("pop stack fail\n"); 254 } 255 visit(&outData); 256 } 257 return 0; 258 }
代码中用到的栈为(4)数据结构——栈(链表)实现 一节中的链表栈,且ElemType的实现同此节。
结果如下:
由于ElemType的限制,用的栈只支持整数运算,如果支持小数运算则需要两个栈的栈元素定义不同才能实现。
目前对除零的操作结果为0,不支持如+4-(-3)之类带正负数的操作