中綴表達式求值


所用知識:C語言,堆棧操作

算法思想來自慕課浙江大學《數據結構》陳老師,何老師

筆記:

1.堆棧:
    1.1 引子    
        一種數據結構,在函數調用,表達式求值等都有廣泛的應用
        中綴表達式:a+b*c-d/e:生活中經常使用,但是計算機不好識別
        后綴表達式:abc*+dc/-:生活中不好使用,但計算機容易識別

        例:
        總結:因此需要一種存儲方法,能順序的存儲運算數,在需要時倒敘輸出。-->堆棧有
        堆棧的特點:后入先出。

    1.2堆棧的操作
    操作集:長度為maxSize的堆棧S ==> stack,堆棧的元素elementType,那么如下的操作
        stack createStack(int maxSize);//生成空堆棧,其最大長度為maxSize
        int isFull(stack s,int maxSize);//判斷棧s是否為空
        void push(stack  s,elementType item);//將元素壓入堆棧
        int isEmpty(stack s);//判斷堆棧是否為空
        elementType pop(stack s);//刪除並返回棧頂元素
        
        1.2.1
            用數組來表示堆棧的方法可以參照文件夾下面的同級目錄的文件stackArray.c
            同時還有使用一個數組來表示兩個堆棧,twoStack.c
        
        1.2.2使用鏈表來模擬堆棧
            因為鏈表有兩頭,但是堆棧的頭指針我們會選擇鏈表的頭節點,因為便於刪除操作,尾節點不方便進行刪除操作
            下面是模擬的示意圖
                head(頭指針,沒有元素的) --> node1(top指針) --> node2(堆棧中的元素) --> node3 --> node4
            代碼可以見:stackLinkList.c

        1.2.3 利用堆棧進行表達式求值
            我們平時所見到的表達式都是中綴表達式,但是如果想求表達式的值,先把中綴表達着中文后綴表達式,
            在利用堆棧對后綴表達式進行求值

            中綴表達式轉后綴表達式的思路;
                首先看一個簡單的:
                中綴表達式:2 + 9 / 3 - 5    后綴表達式:2 9 3 / + 5 -

                通過對比我們發現,中綴表達式和后綴表達式的數字順序並沒有發生改變,只是符號位置發生了改變
                所以,我們可以構思,使用一個棧來存在運算符,遇到數字就輸出,遇到符號就壓棧,如果讀入的符號比棧頂的符號
                優先級高,就壓棧,否則就彈棧並輸出
                下面是中綴表達式:2 + 9 / 3 - 5壓棧和彈棧的具體步驟
                輸出        堆棧內元素
                2           
                           +
                9           
                           /(優先級比+高,壓棧)
                3           
                / +           -(首先彈出/,但是因為同級運行需要按照從左往右的運算,所以+也需要彈棧)
                5           -
                -          (元素全部讀取完畢以后,-彈棧)

                所以中綴表達式:2 + 9 / 3 - 5的后綴表達式為 2 9 3 / + 5 -

                但是如果遇到帶有()的表達式怎么辦:遇到"("把"("壓入棧中,此時棧中的"("運算級別最低,在按照上面的運算符的運算規則來
                進行壓棧和彈棧。直到讀取到")",才把"("彈棧,但是對"("和")"不進行輸出

                舉個例子:
                求下面中綴表達式:2 * (9+6/3-5)+4的后綴表達式

                輸出            堆棧內元素(top->bottom)

                2
                                 *
                                 ( *
                9                 + ( *
                6            
                                 / + ( *
                3                 
                                 - ( *(首先彈出/,但是因為同級運行需要按照從左往右的運算,所以+也需要彈棧,並輸出)
                    / +
                5
                -                *(遇到")",把棧里依次彈出,直到遇到第一個"(",但是對"("和")"不進行輸出)
                *                +(遇到+,先彈出*,然后輸出)
                4                +
                +                NULL(沒有元素可讀,彈出堆棧中最后一個元素)
                
                所以中綴表達式:2 * (9+6/3-5)+4的后綴表達式為: 2 9 6 3 / + 5 - * 4 +

代碼實現

思路:中綴表達式先轉后綴表達式在求值

只支持0-9之間的數字的+-*/以及(),支持括號里面嵌套括號,如3*(2*3-4*1+(3*5)/3)*3+2

不支持分開的括號3*(2*3-4*1+(3*5)/3)*3+2+(4/2)

如果有給出修改意見,感激不盡。寫的比較匆忙,代碼的復用性和注釋的完整性可能不是很好

 

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<math.h>
  5 #include<ctype.h>
  6 #define MAXSIZE 100
  7 
  8 
  9 #include<stdio.h>
 10 #include<stdlib.h>
 11 /*使用鏈表來模擬堆棧,*/
 12 
 13 
 14 
 15 /*=======================================================定義double類型的堆棧鏈式存儲並進行相關的壓棧,彈棧等操作===============================================================*/
 16 
 17 //定義double類型的堆棧的鏈式存儲
 18 typedef struct node3{
 19     double element;
 20     struct node3 *next;
 21 }integerSta,*pIntegerStack;
 22 
 23 //構造一個double類型的鏈式棧
 24 pIntegerStack createIntegerEmptyStack(){
 25     pIntegerStack stack;
 26     stack = (pIntegerStack)malloc(sizeof(integerSta));
 27     if(stack){
 28         stack->next=NULL;
 29     }
 30     return stack;
 31 }
 32 
 33 //判斷double類型的鏈式棧是否為空
 34 int isIntegerEmpty(pIntegerStack stack){
 35     if(stack->next==NULL){
 36         return 1;
 37     }else{
 38         return 0;
 39     }
 40 }
 41 
 42 //壓棧
 43 void pushInteger(pIntegerStack stack,double element){
 44     pIntegerStack node = (pIntegerStack)malloc(sizeof(integerSta));
 45     node->element=element;
 46     node->next=stack->next;
 47     stack->next=node;
 48 }
 49 
 50 //彈棧
 51 double popInteger(pIntegerStack stack){
 52     double element;
 53     pIntegerStack topHead;
 54     if(isEmpty(stack)){
 55         printf("the stack is empty,can not pop");
 56         return;
 57     }else{
 58         topHead = stack->next;
 59         stack->next = topHead->next;
 60         element = topHead->element;
 61         free(topHead);
 62         return element;
 63     }
 64 }
 65 
 66 //取棧頂元素
 67 double getIntegetStackTopElement(pIntegerStack stack){
 68     return (stack->next->element);
 69 }
 70 
 71 //打印棧內元素
 72 void toStringInteger(pIntegerStack stack){
 73     pIntegerStack top = stack->next;
 74     printf("toString:");
 75     while(top!=NULL){
 76         printf("%f ",top->element);
 77         top=top->next;
 78     }
 79     printf("\n");
 80 }
 81 
 82 
 83 
 84 
 85 /*================================================定義一個存儲字符的線性表=========================================================================*/
 86 
 87 
 88 
 89 //構造一個線性表存儲后綴表達式
 90 typedef struct node2{
 91     char data[MAXSIZE];
 92     int length;
 93 }li,*pList;
 94 
 95 
 96 
 97 //初始化線性表
 98 pList createEmptyList(){
 99     pList p;
100     p = (pList)malloc(sizeof(li));
101     if(p){
102         p->length=-1;
103     }
104     return p;
105 }
106 
107 //向線性表中插入元素
108 void insert(pList list,char c){
109     if(list){
110         list->data[++(list->length)]=c;
111     }
112 }
113 
114 //將線性表中的元素打印
115 void toStringList(pList list){
116     int i;
117     for(i=0;i<=list->length;i++){
118         printf("%c ",list->data[i]);
119     }
120     printf("\n");
121 
122 }
123 
124 
125 
126 /*==================================================定義一個字符棧並進行相關操作=================================================================*/
127 //定義字符棧
128 typedef struct node{
129     char element;
130     struct node *next;
131 }sta,*pStack;
132 
133 //創建一個空的字符鏈式棧
134 pStack createEmptyStack(){
135     pStack stack;
136     stack = (pStack)malloc(sizeof(sta));
137     if(stack){
138         stack->next=NULL;
139     }
140     return stack;
141 }
142 
143 //判斷鏈式棧是否為空
144 int isEmpty(pStack stack){
145     if(stack->next==NULL){
146         return 1;
147     }else{
148         return 0;
149     }
150 }
151 
152 //把元素壓入棧
153 void push(pStack stack,char element){
154     pStack node = (pStack)malloc(sizeof(sta));
155     node->element=element;
156     node->next=stack->next;
157     stack->next=node;
158 }
159 
160 //把元素彈棧
161 char pop(pStack stack){
162     char element;
163     pStack topHead;
164     if(isEmpty(stack)){
165         printf("the stack is empty,can not pop");
166         return NULL;
167     }else{
168         topHead = stack->next;
169         stack->next = topHead->next;
170         element = topHead->element;
171         free(topHead);
172         return element;
173     }
174 }
175 
176 //獲取棧頂元素
177 char getTopElement(pStack stack){
178     if(isEmpty(stack)){
179         printf("the stack is empty,can not get top element");
180         return;
181     }else{
182         return (stack->next->element);
183     }
184 }
185 
186 
187 
188 /*=============================================中綴表達式轉后綴表達式=====================================================================*/
189 
190 
191 //打印字符數組
192 void charArrayToString(char *arr){
193     char *p  = arr;
194     while(*p!='\0'){
195         printf("%c ",*p);
196         p = p+1;
197     }
198 }
199 
200 
201 
202 /*
203 判斷字符c是否存在字符數組arr中
204 存在,返回1
205 不存在,返回0
206 */
207 int isCharArrContainChar(char arr[],char c){
208     char *p =arr;
209     while(*p!='\0'){
210         if(*p==c){
211             return 1;
212         }
213         p = p+1;
214     }
215     return 0;
216 }
217 
218 
219 /*給定一個運算符,判斷他的優先級,返回數字越大,優先級越高,例如
220     operatorPriorityArr是一個二維數組,下面是模擬的內容,優先級用戶可以自定義,數組按元素優先級由低到高排列
221         0 +-    第0優先級
222         1 * /   第1優先級
223         2 ()    第2優先級
224 
225     c:等待判斷優先級的運算符
226 */
227 int getCharIndex(char c,char operatorPriorityArr[][MAXSIZE],int length){
228     int i;
229     for(i=0;i<length;i++){
230         if(isCharArrContainChar(operatorPriorityArr[i],c)){
231             return i;
232         }
233     }
234     return -1;
235 }
236 
237 
238 /*
239 判斷字符棧的棧頂元素的優先級和讀入字符的優先級
240 參數:
241     stackTopEle:棧頂元素
242     readChar:讀入的元素
243     operatorPriorityArr:用戶自定義的運算符優先級二維字符數組,數組按元素優先級由低到高排列
244     length:二維數組的行數
245 
246 比較規則:
247     1.如果讀入的字符為"(",那么"("的優先級最高,直接壓棧
248     2.如果棧頂元素為"(",其優先級最低,eadChar直接入棧
249     3.如果讀入的元素readChar優先級大於棧頂元素,則readChar入棧。例如eadChar為"*" topChar為"+",則"*"入棧
250     4.如果讀入的元素readChar優先級小於或者等於(因為運算需要按照從左往右的順序)棧頂元素,則topChar彈棧並輸出(記錄)。
251         再次判斷readChar和當前棧頂元素的優先級,如果當readChar還是優先級小於或者等於當前棧頂元素,接着彈棧並輸出(記錄)。
252         一直比較,直到readChar的優先級大於棧頂元素或者棧為空。
253     5.如果readChar為")",一直彈棧到到第一次遇到"(",並把"("也彈棧,對"("和")"不做輸出,其運算符彈棧且輸出(記錄)
254 
255         返回值:
256             1:壓棧
257             0:讀入")" 直到把第一個"("彈棧棧為止
258             2:彈出當前的棧頂元素,並繼續比較
259 */
260 int compareOperatorChar(char stackTopEle,char readChar,char operatorPriorityArr[][MAXSIZE],int length){
261     int index1,index2;
262     if(stackTopEle=='('){
263         return 1;//棧為空,直接壓棧
264     }else if(readChar==')'){
265         return 0;//讀入遇到")",直到把第一個"("彈棧棧為止
266     }else{
267         //獲取兩個運算符的優先級
268         index1 = getCharIndex(stackTopEle,operatorPriorityArr,length);
269         index2 = getCharIndex(readChar,operatorPriorityArr,length);
270         if(index1<index2){//比較優先級
271             return 1;
272         }else{
273             return 2;
274         }
275     }
276 }
277 
278 
279 /*
280 根據優先級的返回結果進行對應的壓棧和彈棧操作
281 參數:
282     stack:准備好的空字符棧
283     readChar:讀入的字符
284     operatorPriorityArr:定義的優先級規則
285     list:儲存后綴表達式的字符線性表
286     length:二維數組的行數
287 */
288 void comparePriority(pStack stack,char readChar,char operatorPriorityArr[][MAXSIZE],pList list,int length){
289     if(isEmpty(stack)){
290         push(stack,readChar);
291         return 1;
292     }else{
293         char topEle = getTopElement(stack);
294         int result = compareOperatorChar(topEle,readChar,operatorPriorityArr,length);
295         if(result==0){
296             while(getTopElement(stack)!='('){
297                 insert(list,pop(stack));
298             }
299             pop(stack);
300             return;
301         }else if(result==1){
302             push(stack,readChar);
303             return;
304         }else{
305             insert(list,pop(stack));
306             //遞歸再次比較
307             comparePriority(stack,readChar,operatorPriorityArr,list,length);
308         }
309     }
310 }
311 
312 
313 /*
314 根據中綴表達式或者后綴表達式,轉換規則:
315     1.對於數字,進行原樣的輸出或者記錄
316     2.對於運算符,根據優先級進行壓棧彈棧操作,優先級比較規則參照上面
317 參數:
318     stack:准備好的空字符棧
319     arr:中綴表達式的字符數組,支持空格隔開
320     operatorPriorityArr:定義的優先級規則
321     list:儲存后綴表達式的字符線性表
322     length:二維數組的行數
323 */
324 char* getBehindExpress(pStack stack,char *arr,char operatorPriorityArr[3][MAXSIZE],pList list,int length){
325     char *p  = arr;
326     while(*p!='\0'){
327         if(*p>='0' && *p<='9'){
328             insert(list,*p);
329         }else if(*p==' '){
330             p = p+1;
331             continue;
332         }else if(getCharIndex(*p,operatorPriorityArr,length)!=-1){//判斷運算符是否和法
333                 comparePriority(stack,*p,operatorPriorityArr,list,length);
334         }else{
335             printf("the operational character is illegal\n");
336             return "error";
337         }
338         p = p+1;
339     }
340     //當數字讀取完畢后,要把棧里面的運算符全部彈棧
341     while(!isEmpty(stack)){
342         insert(list,pop(stack));
343     }
344 }
345 
346 //打印字符棧里面的元素
347 void toString(pStack stack){
348     pStack top = stack->next;
349     printf("toString:");
350     while(top!=NULL){
351         printf("%c ",top->element);
352         top=top->next;
353     }
354     printf("\n");
355 }
356 
357 
358 
359 
360 /*==================================================計算后綴表達式的值==========================================================================*/
361 /*計算規則如下:
362 求后綴表達式的值
363             6 2 / 3 - 4 2 * + =
364 
365             后綴表達式的求解原理:遇到數值先記錄(壓棧),遇到符號才計算(彈棧兩個元素)。計算離符號最近的兩個數
366             1.6 2 / ==> 3 壓棧
367             2.3 3 - ==> 0 壓棧
368             3.0 4 2 * ==> 0 8
369             4.0 8 + ==>8
370             5.8 = ==> 8
371 */
372 double getValueByAfterExpress(pList list,pStack stack){
373     int i,store;
374     double temp,a1,a2;
375     for(i=0;i<=list->length;i++){
376         //printf("test");
377         char c = list->data[i];
378         if(c>='0' && c<='9'){
379             store = c-'0';//字符轉換為數字
380             temp = store*1.0;//整形轉換為double型
381             //printf("double:%f\n",temp);
382             pushInteger(stack,temp);//數字就壓棧
383         }else{
384             //彈棧並根據運算符做運算
385             double a1 = popInteger(stack);
386             double a2 = popInteger(stack);
387             if(list->data[i]=='+'){
388                 temp = a1+a2;
389                 pushInteger(stack,temp);
390             }
391 
392             if(list->data[i]=='-'){
393                 temp = a2-a1;
394                 pushInteger(stack,temp);
395             }
396             if(list->data[i]=='*'){
397                 temp = a1*a2;
398                 pushInteger(stack,temp);
399             }
400             if(list->data[i]=='/'){
401                 temp = a2/a1;
402                 pushInteger(stack,temp);
403             }
404 
405         }
406         //toStringInteger(stack);
407     }
408     //最終的棧頂元素即為所求的值
409     return getIntegetStackTopElement(stack);
410 }
411 
412 
413 
414 void main(){
415     int n = 3,i;
416     pStack stack  = createEmptyStack();
417     pStack numberStack   = createIntegerEmptyStack();
418     pList list = createEmptyList();
419 
420     char arr1[] = "2 * ( 9 + 6 / 3 - 5 ) + 4";
421     char arr2[] = "2 + 9 / 3 - 5";
422     char arr3[] = "3*(2*3-4*1+(3*5)/3)*3+2";
423     char operatorPriorityArr[3][MAXSIZE]={"+-","*/","()"};
424     //計算二維數組的行數
425     int length=sizeof(operatorPriorityArr)/sizeof(operatorPriorityArr[0]);
426     //char operatorArr[] = "+-*/()";
427     getBehindExpress(stack,arr3,operatorPriorityArr,list,length);
428     //toStringList(list);
429     double value = getValueByAfterExpress(list,numberStack);
430     printf("the express %s caculate result is %f",arr3,value);
431     //double c = (double)('9.23');
432     //printf("test:%f",c);
433     //printf("%d",length);
434 }
表達式求值

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM