閒來無事,寫了一個比較完整的計算器

數學表達式有三種:前綴表達式,中綴表達式,後綴表達式。

中綴表達式就是我們平常見到的,如 4+ 2 * 5 - 7/11 ,這個式子人算起來方便,但是計算機算起來卻計算複雜。對於計算機,最好是將此式轉換爲前綴表達式或後綴表達式後再計算。

在寫計算器之前,首先要知道一點基本知識:

1.中綴表達式轉換爲後綴表達式

舉個例子:

4+ 2 * 5 - 7/11 這是中綴表達式,它的後綴表達式是: 4   2   5   *   7   11   /   -   + .(或者4  2  5  *  +  7  11  /   - )

其實也很簡單,把中綴表達式其實就是表達式對應的二叉樹的中序遍歷,後綴表達式是對應二叉樹的後序遍歷,前綴表達式是前序遍歷。


算法基本思想:
使用三個數組,一個數組保存用戶輸入的表達式(中綴表達式),一個數組保存後綴表達式,一個數組作爲運算符的棧。
從頭到尾掃描中綴表達式,對不同類型的字符按不同情況處理;
1. 如果是數字則直接放入後綴表達式數組;
2. 如果是左括號則直接入棧;
3. 如果是右括號,則把從棧頂直到對應左括號之間的運算符依次退棧[放入後綴表達式數組],並清除對應的左括號;
4. 對於運算符,如果該運算符的優先級大於棧頂優先級,則直接入棧;若該運算符的優先級小於等於棧頂優先級,則先把棧頂運算符出棧,寫入後綴表達式數組,然後再入棧;若該運算符的優先級小於等於棧頂優先級,則先把棧頂運算符出棧,寫入後綴表達式數組,重複此操作,直到該運算符的優先級大於棧頂運算符的優先級,然後將該運算符入棧;(感謝liwenhaosuper的test case,現已將錯誤的地方修正,並修改了相應的代碼)
5. 掃描完成後,取出棧中所有運算符,寫入後綴表達式數組。
注:運算符優先級: *,/   大於    +,-    大於   (       

2.計算後綴表達式
算法思想:
對後綴表達式求值比直接對中綴表達式求值簡單。在後綴表達式中,不需要括號,而且操作符的優先級也不再起作用了。可以通過簡單的出棧如棧操作完成運算。
1. 初始化一個空棧
2. 從左到右讀入後綴表達式
3. 如果字符是一個操作數,把它壓入堆棧。
4. 如果字符是個操作符,彈出兩個操作數,執行恰當操作,然後把結果壓入堆棧。如果您不能夠彈出兩個操作數,後綴表達式的語法就不正確。
5. 到後綴表達式末尾,從堆棧中彈出結果。若後綴表達式格式正確,那麼棧應該爲空。


計算器程序如下:  本程序可計算非負數學表達式,可計算非整數,假設輸入合法,結果保留2位小數

#include <stdio.h>
#include <stdlib.h>
#define MAX_LEN 80
void convert2postfix(char *src,char *dst);
float cal(char *src);

int main(){
    char str1[MAX_LEN],str2[MAX_LEN];
    float res;

    gets(str1);
    convert2postfix(str1,str2);
    printf("src:%s\n",str1);
    printf("dst:%s\n",str2);
    res = cal(str2);
    printf("result:%.2f\n",res);
    return 0;
}

//中綴表達式轉換爲後綴表達式(操作符與操作數之間應有空格隔開)
void convert2postfix(char *src,char *dst){
    char *psrc,*pdst;
    char stack[MAX_LEN];
    int top;

    top = -1;
    psrc = src;
    pdst = dst;
    while (*psrc != '\0') {
        if (*psrc >= '0' && *psrc <= '9') {
            *pdst = *psrc;
            pdst++;
            //加入分隔空格
            if (!(*(psrc+1)>= '0' && *(psrc+1)<= '9') && *(psrc+1)!= '.') {
                *pdst = ' ';
                pdst++;
            }
        }
        if (*psrc == '.') {
            *pdst = *psrc;
            pdst++;
        }
        if (*psrc == '(') {
            stack[++top] = *psrc;
        }
        if (*psrc == ')') {
            while (stack[top] != '(') {
                *pdst = stack[top--];
                pdst++;
                //加入分隔空格
                *pdst = ' ';
                pdst++;
            }
            //彈出'('
            top--;
        }
        if (*psrc == '*' || *psrc == '/') {
            if (stack[top] == '*' || stack[top] == '/') {
                *pdst = stack[top--];
                pdst++;
                //加入分隔空格
                *pdst = ' ';
                pdst++;
            }
            stack[++top] = *psrc;
        }
        if (*psrc == '+' || *psrc == '-') {
            while ( stack[top] == '*'
                    || stack[top] =='/'
                    || stack[top] == '+'
                    || stack[top] == '-') {
                *pdst = stack[top--];
                pdst++;
                //加入分隔空格
                *pdst = ' ';
                pdst++;
            }
            stack[++top] = *psrc;
        }
        psrc++;
    }
    //掃描完成後,取出棧中所有運算符,寫入後綴表達式數組。
    while (top != -1 ) {
        *pdst = stack[top--];
        *pdst++;
        *pdst = ' ';
        pdst++;
    }
    *pdst = '\0';
}

//計算後綴表達式

float cal(char *src){
    float stack[MAX_LEN];
    float opd1,opd2;
    int top;
    char *p,*pre;
    top = -1;

    p = src;
    while (*p != '\0') {
        if (*p >= '0' && *p <= '9') {
            pre = p;
            while ((*p >= '0' && *p <= '9') || *p == '.') {
                p++;
            }
            *p = '\0';
            stack[++top] = atof(pre);
        }
        if (*p == '+' ||*p == '-' ||*p == '*' ||*p == '/' ) {
            opd2 = stack[top--];
            opd1 = stack[top--];
            switch (*p) {
            case '+':
                stack[++top] = opd1+opd2;
                break;
            case '-':
                stack[++top] = opd1-opd2;
                break;
            case '*':
                stack[++top] = opd1*opd2;
                break;
            case '/':
                    //更嚴格一點,應該處理除數爲0的情況
                    stack[++top] = opd1/opd2;
                break;
            }
        }
        p++;
    }
    return stack[top--];
}  


轉載本文請註明作者和出處[Gary的影響力]http://garyelephant.me,請勿用於任何商業用途!

Author: Gary Gao 關注互聯網、分佈式、高併發、自動化、軟件團隊
支持我的工作:  https://me.alipay.com/garygao








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章