1. 程式人生 > >表示式計算器(逆波蘭法)棧操作(C語言實現)

表示式計算器(逆波蘭法)棧操作(C語言實現)

可能很多的同學在學資料結構的時候。說到棧,都會有一道很經典的題目,那就是用棧來實現計算器。我們都知道普通的計算寫起來是很簡單的,但是如果涉及到左右括號以及加減乘除組成的運算式的時候則寫起程式時便不那麼容易了。
比如:(1+(2*(1+3)/2)+10)
面對上面這個表示式,要想寫出這樣一個計算器的程式就顯得比較困難,有一種先進後出的資料結構———棧就可以很好的解決這個問題。

那麼是不是我們有了棧之後就好解決了呢?實際上不是
我們還需要藉助一種叫做逆波蘭表示式(字尾表示式的思想)

知識補充:逆波蘭表示式又叫做字尾表示式。在通常的表示式中,二元運算子總是置於與之相關的兩個運算物件之間,這種表示法也稱為中綴表示。波蘭邏輯學家J.Lukasiewicz
1929年提出了另一種表示表示式的方法,按此方法,每一運算子都置於其運算物件之後,故又稱為字尾表示

哈哈,讓我們來看幾個例子,大家一看就懂了
正常的表示式 逆波蘭表示式

a+b ---------> a,b,+

a+(b-c) -----> a,b,c,-,+

a+(b-c)*d ---> a,b,c,-,d,*,+

a+d*(b-c)---->a,d,b,c,-,*,+

a=1+3 -------> a=1,3 +
最後看一下我們的終極例子
(1+(2*(1+3)/2)+10)(中綴表示式)
 1213+*/+10+(逆波蘭表示式)

可能會有看不懂的人,我來講解一下
首先中綴表示式我們是先將1
+3然後*2然後再/2,然後+1然後+10 逆波蘭呢也是這樣的,只是它表達的方式不一樣。從左往右開始,遇到符號則把符號前面的兩個數字與符號運算,然後再將數字放回。(哈哈,棧的感覺出來了沒有),所以也是先將1+3然後*2然後再/2,然後+1然後+10

下面我們來看程式。程式碼有詳細的註釋,應該可以滿足初學者的需求

/* sqstack.h*/
#ifndef SQSTACK_H_
#define SQSTACK_H_

#define MAXSIZE 32
//棧結構體
typedef struct
{
    int data[MAXSIZE];

    int top;
}sqstack;

sqstack *sqstack_create();  //建立棧
int sqstack_push(sqstack *, int ); //入棧 int sqstack_pop(sqstack *, int *); //出棧 int sqstack_top(sqstack *, int *); //檢視棧頂值 int sqstack_is_empty(sqstack *); //判斷棧是否為控 void sqstack_display(sqstack *); //顯示棧內容,除錯用的 #endif
/* sqstack.c */
#include <stdio.h>
#include <stdlib.h>

#include "sqstack.h"

/* 建立棧 */
sqstack *sqstack_create()
{
    sqstack *L;

    L = (sqstack *)malloc(sizeof(*L));

    L->top = -1;

    return L;
}

/* 入棧 */
int sqstack_push(sqstack *L,int x)
{
    //如果入棧超出棧空間
    if (L->top == MAXSIZE - 1)
    {
    return -1;
    }
    //壓棧
    L->data[++L->top] = x;
    return 0;
}

/* 出棧 */
int sqstack_pop(sqstack *L,int *x)
{
    //如果出棧超出棧空間
    if (L->top == -1)
    {
    return -1;
    }
    //利用傳出引數傳出棧頂元素
    *x = L->data[L->top--];
    return 0;
}

/* 獲得棧頂值 */
int sqstack_top(sqstack *L,int *x)
{
    if (L->top == -1)
    {
    return -1;
    }

    *x = L->data[L->top];
    return 0;
}
/* 判斷棧是否為空 */
int sqstack_is_empty(sqstack *L)
{
    return (L->top == -1);
}

/* 列印輸出棧中元素 */
void sqstack_display(sqstack *L)
{
    int i;
    if (L->top == -1)
    {
    return ;
    }

    for (i = 0 ; i <= L->top; i++)
    {
    printf("%d ",L->data[i]);
    }
    printf("\n");
}
/******************************************************
  日     期: 2015.4.7
  功     能: 計算器的實現:實現加、減、乘、除和左右括號
  作     者: siriuszxn(趙溪楠)
*******************************************************/

#include <stdio.h>
#include "sqstack.h"
#define  MAX 255

//優先順序判斷函式
int get_pri(int ope)
{
    switch(ope)
    {
    case '(':   return 0;
    case '+':
    case '-':   return 1;
    case '*':
    case '/':   return 2;
    default :   return -1;
    }
}

/* 將兩個數出棧、根據ope符號計算,然後再次入棧 */
void compute(sqstack *snum,int ope)
{
    int n,n1,n2;
    //依次獲得數值棧的棧頂兩個數
    sqstack_pop(snum,&n1);
    sqstack_pop(snum,&n2);
    //計算
    switch(ope)
    {
        case '+':   n = n1 + n2; break;
        case '-':   n = n2 - n1; break;
        case '*':   n = n1 * n2; break;
        case '/':   n = n2 / n1; break;
    }
    //計算完成再次入棧
    sqstack_push(snum,n);
}
/* 只有符號才會進入該函式 */
void deal_ope(sqstack *snum,sqstack *sope,int ope)
{
    int old_ope;
    //如果sope符號棧是空棧或者符號為‘(’
    if (sqstack_is_empty(sope) || ope == '(')
    {
        //將括號(入棧
        sqstack_push(sope,ope);
        return ;
    }
    //獲得當前的符號棧的棧頂
    sqstack_top(sope,&old_ope);

    //將當前的符號棧的棧頂符號與傳入的符號進行優先順序比較
    if (get_pri(ope) > get_pri(old_ope))
    {   
        //傳入符號大於當前棧頂,則將傳入符號入棧
        sqstack_push(sope,ope);
        return ;    
    }
    //如果傳入的符號優先順序小於當前棧頂符號
    while (get_pri(ope) <= get_pri(old_ope))
    {
        //將當前棧頂的符號取出與數字棧中頂端的兩個數字進行計算
        sqstack_pop(sope,&old_ope);
        compute(snum,old_ope);
        //如果計算完畢之後符號棧為空則break;
        if (sqstack_is_empty(sope))
        {
            break;
        }
        //再次取出一個當前棧符號與傳入符號比較
        sqstack_top(sope,&old_ope);
    }
    //進行完畢優先順序計算之後,再將新傳入的符號入棧
    sqstack_push(sope,ope);
}
/*如果檢測到符號時')',則執行該函式,引數為數字棧和符號棧*/
void deal_bracket(sqstack *snum,sqstack *sope)
{
    int old_ope;
    //獲得當前的符號棧的棧頂符號
    sqstack_top(sope,&old_ope);
    //直到找到預期配對的左括號。因為正確的算式中左右括號一定是配對的
    while (old_ope != '(')
    {
        //當前符號出棧然後將數字出棧兩個進行計算,在括號內優先順序最高,
        sqstack_pop(sope,&old_ope);
        compute(snum,old_ope);
        //然後再次取出當前符號棧棧頂符號,至到出現‘(’
        sqstack_top(sope,&old_ope);
    }
    //最後將出現的左擴號出棧丟棄
    sqstack_pop(sope,&old_ope);
}


int main()
{
    /*str為表示式陣列*/
    char str[MAX];
    printf("請輸入你要計算的表示式:\n");
    gets(str);

    int i = 0;
    int value = 0;   //數字的值
    int flag = 0;    
    int old_ope;

    sqstack *snum,*sope;      // 定義兩個指向棧結構體的指標

    snum = sqstack_create();  // 建立存放數字的棧

    sope = sqstack_create();  // 建立存放運算子號的棧

    /* 表示式字串解析函式,然後將高優先順序的符號/(*)進行計算重新入棧
       退出while大家的優先順序都一樣+-*/
    while (str[i] != '\0')
    {
        //獲取輸入的數字
        if (str[i] >= '0' && str[i] <= '9')//num
        {
            value = value * 10 + str[i] - '0';
            flag = 1;
        }
        else//ope
        {
            if (flag)
            {
                //flag = 1說明value裡面儲存了數字,將其入棧
                sqstack_push (snum, value);
                //num標誌清零,value存放數字的變數清零
                flag = 0;
                value = 0;
            }
            if(str[i] == ')')
            {
                //如果是右括號,則
                deal_bracket(snum,sope);
            }
            else//+-*/(等情況 
            {
                deal_ope(snum,sope,str[i]);
            }   
        }   
        i++;
    }
    //如果flag = 1.說明value裡面還有數值,將其入棧 
    if (flag)
    {
        sqstack_push(snum,value);
    }
    //然後將符號與數字依次出棧計算。數字出棧計算完成之後回再次在compute中入棧
    while (!sqstack_is_empty(sope))
    {
        sqstack_pop(sope,&old_ope);
        compute(snum,old_ope);
    }
    //取出數字棧最後剩下的數字,就是最後的答案
    sqstack_pop(snum,&value);
    //列印結果
    printf("%s = %d\n",str,value);

    return 0;
}

演示結果!!!!!!

這裡寫圖片描述