您當前的位置:首頁 > 曲藝

如何利用棧實現表示式求值

作者:由 守望 發表于 曲藝時間:2019-03-27

前言

假如要你實現一個可以識別表示式的簡易計算器,你會怎麼實現?例如使用者輸入:

3 + 5 * (2 - 4)

可以直接得出計算結果:-7。對於人類來說,我們很容易計算出來,因為我們從左往右看,看到後面括號時,知道括號內的計算優先順序最高,因此可以先計算括號內的,然後反過來計算乘法,最後計算加法,得到最終結果。

字尾表示式

而對於計算機來說,實際也可以採用類似的順序,先記錄儲存3為a,然後儲存5為b,計算2-4結果存入c,再然後計算b*c儲存d,最終計算a+d得到最終結果。而這種計算過程的操作順序可描述如下(把運算子號放在運算元後面):

3 5 2 4 - * +

這種記法叫做字尾或逆波蘭記法(而我們平常見到的叫中綴記法),它的特點是

不需要用括號就能表示出整個表示式哪部分運算先進行,也就是說不需要考慮優先順序,這非常符合計算機的處理方式。

這種記法很容易使用我們前面介紹的棧來求值,但是前提是需要將中綴表示式先轉換為字尾表示式。對於這種轉換,我們也可以使用前面介紹的棧-C語言實現或者將要介紹的樹來完成,因篇幅有限,本文不準備介紹。

接下來將會介紹如何利用中綴表示式進行求值。

利用棧實現中綴表示式求值

前面也說到,所謂中綴表示式,就是我們能看到的正常表示式,中綴表示式求值,也就是直接對輸入的表示式進行求值。為簡單起見,我們這裡假設

只涉及加減乘除和小括號,並且運算元都是正整數,不涉及更加複雜的資料或運算。

計算思路:

使用兩個棧,stack0用於儲存運算元,stack1用於儲存運算子

從左往右掃描,遇到運算元入棧stack0

遇到運算子時,如果優先順序低於或等於棧頂運算子優先順序,則從stack0彈出兩個元素進行計算,並壓入stack0,繼續與棧頂運算子的比較優先順序

如果遇到運算子高於棧頂運算子優先順序,則直接入棧stack1

遇到左括號,直接入棧stack1,遇到右括號,則直接出棧並計算,直到遇到左括號

上面的思路可能看起來不是很明確,我們舉一個簡單的例子,假如要對下面的表示式求值:

6 * (2 + 3 )* 8 + 5

我們從左往右開始掃描。首先遇到運算元‘6’,和運算子‘*’,分別入棧

stack0:

棧頂

6

stack1:

棧頂

*

繼續往後掃描,遇到‘(’直接入棧,‘2’入棧,棧頂是左括號,’+‘入棧,‘3’入棧

stack0:

棧頂

623

stack1:

棧頂

*(+

繼續往後掃描,遇到右括號,它與棧頂運算子‘+’相比,優先順序要高,因此,將‘+’出棧,彈出兩個運算元‘3’,‘2’,計算結果得到‘5’,併入棧:

stack0:

棧頂

65

stack1:

棧頂

*(

繼續出棧,直到遇到左括號

stack0:

棧頂

65

stack1:

棧頂

*

繼續往後掃描,遇到運算子‘

’,優先順序與棧頂‘

’優先順序相同,因此彈出運算元並計算得到30入棧,最後‘*’入棧

stack0:

棧頂

30

stack1:

棧頂

*

繼續掃描,‘8’入棧,運算子‘+’優先順序小於‘*’,彈出運算元計算得到結果‘240’,並將其入棧,最後‘+’也入棧

stack0:

棧頂

240

stack1:

棧頂

+

最後‘5’入棧,發現運算子棧不為空,彈出運算子‘+’和兩個運算元,並進行計算,得到‘245’,入棧,得到最終結果。

stack0

棧頂

245

stack1:

程式碼實現

#include

#include

#define STACK_SIZE 64 /*棧大小*/

#define TOP_OF_STACK -1 /*棧頂位置*/

typedef int ElementType; /*棧元素型別*/

#define SUCCESS 0

#define FAILURE -1

/*定義棧結構*/

typedef struct StackInfo

{

int topOfStack; /*記錄棧頂位置*/

ElementType stack[STACK_SIZE]; /*棧陣列,也可以使用動態陣列實現*/

}StackInfo_st;

/*函式宣告*/

int stack_push(StackInfo_st *s,ElementType value);

int stack_pop(StackInfo_st *s,ElementType *value);

int stack_top(StackInfo_st *s,ElementType *value);

int stack_is_full(StackInfo_st *s);

int stack_is_empty(StackInfo_st *s);

/*入棧,0表示成,非0表示出錯*/

int stack_push(StackInfo_st *s,ElementType value)

{

if(stack_is_full(s))

return FAILURE;

/*先增加topOfStack,再賦值*/

s->topOfStack++;

s->stack[s->topOfStack] = value;

return SUCCESS;

}

/*出棧*/

int stack_pop(StackInfo_st *s,ElementType *value)

{

/*首先判斷棧是否為空*/

if(stack_is_empty(s))

return FAILURE;

*value = s->stack[s->topOfStack];

s->topOfStack——;

return SUCCESS;

}

/*訪問棧頂元素*/

int stack_top(StackInfo_st *s,ElementType *value)

{

/*首先判斷棧是否為空*/

if(stack_is_empty(s))

{

printf(“stack is empty”);

return FAILURE;

}

*value = s->stack[s->topOfStack];

return SUCCESS;

}

/*判斷棧是否已滿,滿返回1,未滿返回0*/

int stack_is_full(StackInfo_st *s)

{

return s->topOfStack == STACK_SIZE - 1;

}

/*判斷棧是否為空,空返回1,非空返回0*/

int stack_is_empty(StackInfo_st *s)

{

return s->topOfStack == - 1;

}

/*用於記錄符號的優先順序,這裡浪費了一些記憶體,可以最佳化*/

static char priority[128] = {0};

void priorityInit()

{

/*初始化優先順序,值越小,優先順序越高*/

priority[‘+’] = 4;

priority[‘-’] = 4;

priority[‘*’] = 3;

priority[‘/’] = 3;

priority[‘(’] = 1;

priority[‘)’] = 1;

}

/*比較運算子的優先順序,op1優先順序大於op2時,返回大於0的值*/

int priorityCompare(char op1,char op2)

{

return priority[op2] - priority[op1];

}

/*出棧運算子和運算元進行計算*/

int calcOp(StackInfo_st *nums,StackInfo_st *ops,int nowOp)

{

int a ,b,op;

stack_pop(ops,&op);

printf(“op %c is <= %c\n”,nowOp,op);

printf(“get op from stack %c\n”,op);

if(SUCCESS != stack_pop(nums,&b))

{

printf(“pop failed\n”);

return -1;

}

if(SUCCESS != stack_pop(nums,&a))

{

printf(“pop failed\n”);

return 0;

}

printf(“get b from stack %d\n”,b);

printf(“get a from stack %d\n”,a);

switch(op)

{

case ‘+’:

{

printf(“push %d into stack\n”,a+b);

stack_push(nums,a+b);

break;

}

case ‘-’:

{

stack_push(nums,a-b);

break;

}

case ‘*’:

{

printf(“push %d into stack\n”,a*b);

stack_push(nums,a*b);

break;

}

case ‘/’:

{

printf(“push %d into stack\n”,a/b);

stack_push(nums,a/b);

break;

}

}

return 1;

}

int calc(const char* exp,int *result)

{

if(NULL == exp || NULL == result)

return FAILURE;

/*建立棧,用於儲存數*/

StackInfo_st nums;

nums。topOfStack = TOP_OF_STACK;

/*用於儲存運算子*/

StackInfo_st ops;

ops。topOfStack = TOP_OF_STACK;

int index = 0;

/*用於標記,判斷上一個是否為數字*/

int flag = 0;

int temp = 0;

int op ;

while(0 != *exp)

{

/*如果是數字*/

if(isdigit(*exp))

{

printf(“char is %c\n”,*exp);

/*如果上一個還是數字,則取出棧頂資料*/

if(1 == flag)

{

stack_pop(&nums,&temp);

printf(“pop from stack num %d\n”,temp);

}

else

temp = 0;

flag = 1;

temp = 10 * temp + *exp-‘0’;

printf(“push %d to stack\n”,temp);

stack_push(&nums,temp);

}

/*如果是運算子*/

else if(‘/’ == *exp || ‘*’ == *exp || ‘+’ == *exp || ‘-’ == *exp)

{

flag = 0;

printf(“OP is %c\n”,*exp);

while((ops。topOfStack > TOP_OF_STACK )&&(SUCCESS == stack_top(&ops,&op))&&‘(’ != op && ‘)’!=op&&(priorityCompare(*exp,op) < 0))

{

calcOp(&nums, &ops,*exp);

}

printf(“push %c to stack ops\n”,*exp);

stack_push(&ops,*exp);

}

/*左括號直接入棧*/

else if(‘(’ == *exp )

{

printf(“push ( to stack ops\n”);

flag = 0;

stack_push(&ops,*exp);

}

/*右括號,計算*/

else if(‘)’ ==*exp )

{

printf(“deal with ) in ops\n”);

flag = 0;

/*右括號時,不斷計算,直到遇見左括號*/

while(SUCCESS == stack_top(&ops,&op) && ‘(’ != op)

{

calcOp(&nums, &ops,*exp);

}

stack_pop(&ops,&op);

}

else

{

flag=0;

}

printf(“flag is %d\n\n”,flag);

exp++;

}

/*計算剩餘兩個棧的內容*/

while((!stack_is_empty(&ops)) && (!stack_is_empty(&nums)))

{

if(!calcOp(&nums, &ops,0))

printf(“exp is error\n”);

}

stack_pop(&nums,&temp);

/*如果棧中還有內容,說明表示式錯誤*/

if((!stack_is_empty(&ops)) || (!stack_is_empty(&nums)))

printf(“\n\nexp is ok\n\n”);

if(SUCCESS == stack_pop(&nums,&temp))

printf(“result is %d\n”,temp);

*result = temp;

return 0;

}

int main(int argc ,char *argv[])

{

int result;

priorityInit();

char exp[] = “6 * (2 + 3 * 3)* 8 + 5”;

calc(exp,&result);

printf(“result is %d\n”,result);

return 0;

}

總結

本文介紹了利用棧對中綴表示式進行求值,而程式碼實現還有很多不足之處,例如對錶達式的正確性校驗不足,只能處理正整數等等,歡迎在此基礎上完善補充。儘管如此,整個過程對使用棧進行中綴表示式的求值做了一個較為完整的介紹,因此具有一定的參考性。

微信公眾號【程式設計珠璣】:專注但不限於分享計算機程式設計基礎,Linux,C語言,C++,資料結構與演算法,工具,資源等程式設計相關[原創]技術文章,號內包含大量經典電子書和影片學習資源。歡迎一起交流學習,一起修煉計算機“內功”,知其然,更知其所以然。

如何利用棧實現表示式求值

標簽: stack  int  棧頂  EXP  printf