1. 程式人生 > >使用棧實現表示式求值

使用棧實現表示式求值

看書學了一晚上這個內容,終於實現了

分為三個步驟:

  0. 檢查輸入是否有誤(因為輸入其他的非預期字元,程式就會崩潰,我就試著加了一個檢查輸入的函式)

  1. 先將正常的中綴表示式轉換為字尾表示式

  2. 再進行求值

根據字尾表示式求值比較簡單,因為字尾表示式已經有了優先順序。

比較難懂的是將中綴表示式轉換為字尾表示式,需要考慮很多情況:

  1. 如果字元是 '(' ,就直接入操作符棧,因為越內層的括號優先順序越高,所以不用考慮什麼

  2. 如果字元是 ')' ,就要結束一個左括號了,將前面的操作符出棧送入postexp,直到遇到 '('。

  3. 如果字元是 '-' 或 '+' ,就要將遇到的第一個 '(' 之前的所有操作符出棧送入postexp。因為相對來說,後面的 '+' 和 '-' 優先順序是最低的,低於前面的所有操作符。最後出棧完,再將它壓入棧。

  4. 如果字元是 '*' 或 '/' , 先判斷前面操作符棧底是否為 '*' 或 '/' ,是的話就要將棧裡的符號出棧送入postexp。因為 '*' 或 '/' 的優先順序低於前面的 '*' 或 '/',是高於前面的  '+' 和 '-'  的。

 

#include <iostream>
using namespace std;
const int MAXSIZE = 50;

typedef struct
{
    char data[MAXSIZE];
    int top;
}Stack;

void InitStack(Stack *&s)
{
    s = (Stack*)malloc(sizeof(Stack));
    s->top = -1;
}

bool Push(Stack *&s, char e)
{
    if (s->top == MAXSIZE - 1)
        return false;
    s->top++;
    s->data[s->top] = e;
    return true;
}

bool Pop(Stack *&s, char &e)
{
    if (s->top == -1)
        return false;
    e = s->data[s->top];
    s->top--;
    return true;
}

bool GetTop(Stack *&s, char &e)
{
    if (s->top == -1)
        return false;
    e = s->data[s->top];
    return true;
}

bool StackEmpty(Stack *&s)
{
    if (s->top == -1)
        return true;
    return false;
}


void trans(char *exp, char postexp[])
{
    int i = 0; // postexp 下標
    char e;    // 給 pop gettop用
    Stack *Optr;
    InitStack(Optr);

    while (*exp != '\0')
    {
        switch (*exp)
        {
        case '(':
            Push(Optr, '(');
            exp++; //exp指標前移,繼續處理下一個字元
            break;
        case ')': //右括號時,一直出棧,指導遇到 (
            Pop(Optr, e);
            while (e != '(' ) // 當 e='(' 時,正好把它丟棄了
            {
                postexp[i++] = e;
                Pop(Optr, e);
            }
            exp++;
            break;
        case '+':
        case '-': // + - 優先順序相同,當做同一種情況處理
            while (!StackEmpty(Optr)) //操作符棧只要不為空,則一直掃描出棧,直到遇到 ) 。
            {
                GetTop(Optr, e);
                if (e == '(') //後面的 + - ,優先順序最低,最後考慮;如果e是 (,則直接入棧即可,所以break
                    break;
                else
                {
                    postexp[i++] = e;
                    Pop(Optr, e);
                }
            }
            Push(Optr, *exp); //最後將 + - 入棧
            exp++;
            break;
        case '*':
        case '/':
            while (!StackEmpty(Optr))
            {
                GetTop(Optr, e);
                if (e == '/' || e == '*') // * / 的優先順序僅僅低於它前面的 * /,高於前面的 + -,所以要將前面的 * / 彈出棧;+ - 保留,因為新的 * / 會放在棧低,優先順序高。
                {
                    postexp[i++] = e;
                    Pop(Optr, e);
                }
                else
                    break; // 其他情況( + - 左括號 )退出,
            }
            Push(Optr, *exp); //最後將 / * 入棧
            exp++;
            break;
        default:
            while (*exp > '0' && *exp < '9') //迴圈判斷是否為數字字元,如果是則儲存到postexp,迴圈判斷是因為可能是多位數字
            {
                postexp[i++] = *exp;
                exp++;
            }
            postexp[i++] = '#'; //以#標誌一個數字字串結束
        } 
    }
    while (!StackEmpty(Optr)) //掃描完exp後,操作符棧可能還有操作符,將其存到postexp
    {
        Pop(Optr, e);
        postexp[i++] = e;
    }
    postexp[i] = '\0'; //結束字串
    free(Optr); //銷燬棧
}

//--------- 下面是針對數字型棧的

typedef struct
{
    double data[MAXSIZE];
    int top;
}Stack_num;

void InitStack_num(Stack_num *&s)
{
    s = (Stack_num *)malloc(sizeof(Stack_num));
    s->top = -1;
}

bool Push_num(Stack_num *&s, double e)
{
    if (s->top == MAXSIZE - 1)
        return false;
    s->top++;
    s->data[s->top] = e;
    return true;
}

bool Pop_num(Stack_num *&s, double &e)
{
    if (s->top == -1)
        return false;
    e = s->data[s->top];
    s->top--;
    return true;
}


//---------

double compvalue(char *postexp)
{
    Stack_num *num; //運算元棧
    InitStack_num(num);
    double result; //結果
    double a, b; //彈出棧的兩個數
    double c; //計算彈出棧的兩個數
    double d;    //將連續的數字字元轉換成整數儲存在d裡
    while (*postexp != '\0')
    {
        switch (*postexp)
        {
        case '+':
            Pop_num(num, a); //因為字尾表示式已經有了優先順序了,所以可以直接彈出兩個數進行計算
            Pop_num(num, b);
            c = a + b;
            Push_num(num, c);
            break;
        case '-':
            Pop_num(num, a);
            Pop_num(num, b);
            c = b - a; //注意是b-a,因為a先出來,是後面的數字
            Push_num(num, c);
            break;
        case '*':
            Pop_num(num, a);
            Pop_num(num, b);
            c = a * b;
            Push_num(num, c);
            break;
        case '/':
            Pop_num(num, a); // a是除數
            Pop_num(num, b);
            if (a != 0)
            {
                c = b / a;
                Push_num(num, c);
            }
            else
            {
                cout << "除0錯誤!" << endl;
                exit(0);
            }
            break;
        default:
            d = 0;
            while (*postexp >= '0' && *postexp <= '9') //當 *postexp = # 時,就忽略了。
            {
                d = 10 * d + (*postexp - '0');
                postexp++;
            }
            Push_num(num, d);
        }
        postexp++; //繼續下一個字元
    }
    Pop_num(num, result);

    return result;
}

bool test(char *exp)
{
    // start - 是否非法字元
    for (int i = 0; exp[i] != '\0'; i++)
    {
        if ( !((exp[i] >= '0' && exp[i] <= '9') || exp[i] == '+' || exp[i] == '-' || \
            exp[i] == '*' || exp[i] == '/' || exp[i] == '(' || exp[i] == ')') )

            return false;
    }
    // end - 是否非法字元

    // start - 括號是否匹配
    Stack *s;
    char e;
    InitStack(s);
    for (int i = 0; exp[i] != '\0'; i++)
    {
        switch (exp[i])
        {
        case '(':
            Push(s, exp[i]);
            break;
        case ')':
            if (Pop(s, e))
            {
                if (exp[i] == ')' && e == '(')
                    return true;
                else
                    return false;
            }
            else
                return false;
            break;
        }
    }
    if (s->top != -1)  //棧為空才認為成功
        return false;
    else
        return true;

    // end - 括號匹配

    return true;
}

int main()
{
    char exp[MAXSIZE];
    char postexp[MAXSIZE];
    
    while (true)
    {
        cout << "輸入表示式:";
        
        cin >> exp;
        if (!test(exp))
        {
            cout << "非法字元 或 括號不匹配!" << endl;
            continue;
        }
        trans(exp, postexp);

        cout << "字尾表示式:" << postexp << endl;
        cout << "結果:" << compvalue(postexp) << endl;
    }

    system("pause");
    return 0;
}