逆波蘭表達式求值

逆波蘭表達式又叫做後綴表達式。逆波蘭表示法是波蘭邏輯學家J・盧卡西維茲(J・ Lukasewicz)於1929年首先提出的一種表達式的表示方法 [1] 。後來,人們就把用這種表示法寫出的表達式稱作“逆波蘭表達式”。逆波蘭表達式把運算量寫在前面,把算符寫在後面。

逆波蘭表達式就是後綴表達式,將運算量寫在前面,運算符寫在後面,如:
2, 1, +, 3, * 等價於 ((2 + 1) * 3)

下面我們就來實現逆波蘭表達式的求值。

對於上面的逆波蘭表達式如何理解呢?因爲+ - * /都是二元操作符,我們首先需要讀取兩個操作對象,在讀取操作的符號,計算完後再將計算結果填入棧中,進行下一次的計算。

所以我們需要用到一個數字棧,來保存所有的運算對象,然後每當我們遇到一個運算符,就彈出兩個數字,再將計算結果填入,等到棧中只剩一個元素的時候,那個元素就是計算結果。

在這裏逆波蘭表達式使用二維字符數組的形式來表示的,所以我們第一步就需要將所有的數字識別出來

bool isnum(const char* str)
{
    if(*str == '-' && strlen(str) == 1)
        return false;

    if(*str == '*' || *str == '/' || *str == '+')
        return false;

    return true;
}

在這裏有一個問題,就是負數容易和減號衝突,我們讀取的時候有可能將數字前面的減號當作數字讀取,所以我們可以通過判斷字符串中是不是僅有減號沒有別的數字,來判斷這個到底是減號還是負數。

int getnum(const char* str)
{
    int i = 0;
    int ret = 0, flag = 1;
    if(str[i] == '-')
    {
        i++;
        flag = 0;
    }

    for(; i < strlen(str); i++)
    {
        ret = ret * 10 + str[i] - '0';
    }

    return flag ? ret : - ret;
}

判斷完是否是數字後就需要將其從字符串轉換爲數字,如果第一位是負號,我們就用flag = 0表示,到返回的時候返回負數即可。字符串轉換爲數字只需要減去一個字符‘0’,但是有一個問題,就是如果數據不僅僅只有一位,所以我們還需要一個循環來把字符串轉換爲多位數。

int evalRPN(char ** tokens, int tokensSize){
    int count, num1, num2, j = 0;
    int *stack = (int*)malloc(sizeof(int) * tokensSize);

    for(int i = 0; i < tokensSize; i++)
    {
        if (isnum(tokens[i]))
        {
            stack[j++] = getnum(tokens[i]);
        }
        else
        {
            switch(*tokens[i])
            {
                case '+': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    stack[j++] = num2 + num1;
                    break;

                case '-': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    stack[j++] = num2 - num1;
                    break;    

                case '*': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    stack[j++] = num2 * num1;
                    break;          

                case '/': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    if(!num1)
                        break;
                    stack[j++] = num2 / num1;
                    break;
            }
        }
    }
    
    count = stack[j - 1];
    return count;
}

當我們遍歷字符串時,每當我們碰到數字就將數據推入數字棧,每當我們碰到運算符時就彈出兩個數字,然後計算完後再存入數字棧中,當字符串遍歷完後,數字棧僅剩的那一個數據就是計算結果。

這裏可能出現錯誤的地方就是兩個數據運算時的位置。先彈出的是右運算數,後彈出的是左運算數。

下面來測試一個數據:
10, 6, 9, 3, +, -11, *, /, *, 17, +, 5, +
等價於
((10 * (6 / ((9 + 3) * -11))) + 17) + 5

在這裏插入圖片描述
結果正確

完整代碼

bool isnum(const char* str)
{
    if(*str == '-' && strlen(str) == 1)
        return false;

    if(*str == '*' || *str == '/' || *str == '+')
        return false;

    return true;
}

int getnum(const char* str)
{
    int i = 0;
    int ret = 0, flag = 1;
    if(str[i] == '-')
    {
        i++;
        flag = 0;
    }

    for(; i < strlen(str); i++)
    {
        ret = ret * 10 + str[i] - '0';
    }

    return flag ? ret : - ret;
}

int evalRPN(char ** tokens, int tokensSize){
    int count, num1, num2, j = 0;
    int *stack = (int*)malloc(sizeof(int) * tokensSize);

    for(int i = 0; i < tokensSize; i++)
    {
        if (isnum(tokens[i]))
        {
            stack[j++] = getnum(tokens[i]);
        }
        else
        {
            switch(*tokens[i])
            {
                case '+': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    stack[j++] = num2 + num1;
                    break;

                case '-': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    stack[j++] = num2 - num1;
                    break;    

                case '*': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    stack[j++] = num2 * num1;
                    break;          

                case '/': 
                    num1 = stack[--j];
                    num2 = stack[--j];
                    if(!num1)
                        break;
                    stack[j++] = num2 / num1;
                    break;
            }
        }
    }
    
    count = stack[j - 1];
    return count;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章