數據結構(二)棧與隊列---棧的應用:四則運算實現


棧的應用:四則運算實現

注:我們這里使用鏈棧來實現,當然前面的順序棧同樣可以實現,而且更加容易理解。這里我們使用鏈棧來練習

(一)預備知識

前綴、中綴、后綴表達式(逆波蘭表達式)

union聯合體使用詳解

前綴表達式、中綴表達式、后綴表達式都是四則運算的表達方式,用以四則運算表達式求值

中綴表達式

中綴表達式就是常見的運算表達式,如(3+4)×5-6

前綴表達式

前綴表達式又稱波蘭式,前綴表達式的運算符位於操作數之前
比如:- × + 3 4 5 6

后綴表達式(逆波蘭表達式)<這是我們使用的>

后綴表達式又稱逆波蘭表達式,與前綴表達式相似,只是運算符位於操作數之后
比如:3 4 + 5 × 6 -

(二)中綴轉后綴

例如,將中綴表達式“1+((2+3)×4)-5”轉換為后綴表達式的過程如下

掃描到的元素 s2(棧底->棧頂) s1 (棧底->棧頂) 說明
1 1 數字,直接入棧
+ 1 + s1為空,運算符直接入棧
( 1 + ( 左括號,直接入棧
( 1 + ( ( 同上
2 1 2 + ( ( 數字
+ 1 2 + ( ( + s1棧頂為左括號,運算符直接入棧
3 1 2 3 + ( ( + 數字
) 1 2 3 + + ( 右括號,彈出運算符直至遇到左括號
× 1 2 3 + + ( × s1棧頂為左括號,運算符直接入棧
4 1 2 3 + 4 + ( × 數字
) 1 2 3 + 4 × + 右括號,彈出運算符直至遇到左括號
- 1 2 3 + 4 × + - -與+優先級相同,因此彈出+,再壓入-
5 1 2 3 + 4 × + 5 - 數字
到達最右端 1 2 3 + 4 × + 5 - s1中剩余的運算符

因此結果為“1 2 3 + 4 × + 5 -”

步驟:

  1. 初始化兩個棧:運算符棧s1和儲存中間結果的棧s2;
  2. 從左至右掃描中綴表達式;
  3. 遇到操作數時,將其壓s2;
  4. 遇到運算符時,比較其與s1棧頂運算符的優先級:
    1. 如果s1為空,或棧頂運算符為左括號“(”,則直接將此運算符入棧;
    2. 否則,若優先級比棧頂運算符的高,也將運算符壓入s1(注意轉換為前綴表達式時是優先級較高或相同,而這里則不包括相同的情況);
    3. 否則,將s1棧頂的運算符彈出並壓入到s2中,再次轉到(4-1)與s1中新的棧頂運算符相比較;
  5. 遇到括號時:
    1. 如果是左括號“(”,則直接壓入s1;
    2. 如果是右括號“)”,則依次彈出s1棧頂的運算符,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄;
  6. 重復步驟2至5,直到表達式的最右邊;
  7. 將s1中剩余的運算符依次彈出並壓入s2;
  8. 依次彈出s2中的元素並輸出,結果的逆序即為中綴表達式對應的后綴表達式(轉換為前綴表達式時不用逆序)

(三)計算后綴表達式結果

從左至右掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對它們做相應的計算(次頂元素 op 棧頂元素),並將結果入棧;重復上述過程直到表達式最右端,最后運算得出的值即為表達式的結果

例如后綴表達式“3 4 + 5 × 6 -”

  1. 從左至右掃描,將3和4壓入堆棧;
  2. 遇到+運算符,因此彈出4和3(4為棧頂元素,3為次頂元素,注意與前綴表達式做比較),計算出3+4的值,得7,再將7入棧;
  3. 將5入棧;
  4. 接下來是×運算符,因此彈出5和7,計算出7×5=35,將35入棧;
  5. 將6入棧;
  6. 最后是-運算符,計算出35-6的值,即29,由此得出最終結果。

(四)代碼實現

//棧中數據類型
typedef struct Data
{
    int flag;    //1為字符,2為浮點數
    union 
    {
        double num;
        char sign;
    }number;
}data;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define STACK_INIT_SIZE 100    //定義棧的初始大小
#define STACK_INCR_SIZE 10    //定義棧的增長大小

#define MAXSIZE 100    //中綴表達式的長度

typedef struct Data
{
    int flag;    //1為字符,2為浮點數
    union 
    {
        double num;
        char sign;
    }number;
}data;

typedef data ElemType;
typedef int Status;

typedef struct
{
    ElemType *base;    //棧底指針
    ElemType *top;    //棧頂指針
    int stackSize;    //最大容量,這是可修改的
}sqStack;



//四個基礎操作
Status InitStack(sqStack *s);    //初始化操作,建立一個空棧
Status ClearStack(sqStack *s);    //將棧清空
Status StackEmpty(sqStack s);    //若棧存在,返回true,否則返回false
int StackLength(sqStack s);        //返回棧S的元素個數

Status GetTop(sqStack s, ElemType *e);    //若是棧存在且非空,用e返回S的棧頂元素
Status Push(sqStack *s, ElemType e);    // 若是棧存在,則插入新的元素e到棧S中並成為棧頂元素
Status Pop(sqStack *s, ElemType *e);    //若是棧存在且非空,刪除棧頂元素,並用e返回其值
Status DestroyStack(sqStack *s);        //若是棧存在,則銷毀他

void PrintStack(sqStack s);    //打印所有數據,測試用,會依次將數據出棧


//初始化操作,建立一個空棧
Status InitStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE*sizeof(ElemType));
    if (!s->base)
        return ERROR;
    s->top = s->base;    //最開始,棧頂就是棧底
    s->stackSize = STACK_INIT_SIZE;
    return OK;
}

//將棧清空,將棧頂指針移動到棧底即可,容量大小不要修改,數據不需要清空,數據入棧會覆蓋
Status ClearStack(sqStack *s)
{
    if (s == NULL)
        return ERROR;
    s->top = s->base;
    return OK;
}

//若棧存在,返回true,否則返回false
Status StackEmpty(sqStack s)
{
    if (s.base == s.top)
        return TRUE;
    return FALSE;
}

//返回棧S的元素個數
int StackLength(sqStack s)
{
    int length = s.top - s.base;    //指針之間運算,是按照其中數據大小字節來算的
    return length;
}

//若是棧存在且非空,用e返回S的棧頂元素,注意:只是獲取棧頂數據,不出棧
Status GetTop(sqStack s, ElemType *e)
{
    if (!e || StackEmpty(s) || !s.base)
        return ERROR;
    *e = *(s.top - 1);
    return OK;
}

//入棧操作:若是棧存在,則插入新的元素e到棧S中並成為棧頂元素
Status Push(sqStack *s, ElemType e)
{
    ElemType* newStack;
    if (!s->base)
        return ERROR;

    if (s->top-s->base>=s->stackSize)    //棧滿,需要再分配
    {
        newStack = (ElemType *)realloc(s->base, (s->stackSize + STACK_INCR_SIZE)*sizeof(ElemType));    //重新分配大小
        if (!newStack)    //若是分配失敗,會返回NULL
        {
            free(s->base);
            exit(0);    //分配失敗,直接退出
        }
        s->base = newStack;
        //分配后需要將棧頂指針進行移動到新的位置
        s->top = s->base + s->stackSize;
    }
    *(s->top) = e;
    s->top++;
    return OK;
}

//若是棧存在且非空,刪除棧頂元素(只需要將棧頂指針下移即可),並用e返回其值
Status Pop(sqStack *s, ElemType *e)
{
    if (!s->base || !e || StackEmpty(*s))
        return ERROR;
    *e = *(--s->top);
    return OK;
}

//若是棧存在,則銷毀他(直接將棧底指針釋放即可,置為空)
Status DestroyStack(sqStack *s)
{
    if (!s->base)    //若是棧存在
    {
        s->stackSize = 0;
        free(s->base);
        s->base = s->top = NULL;
    }
    return OK;
}

//打印數據
void PrintStack(sqStack s)
{
    data d;
    int len = StackLength(s);
    for (int i = 0; i < len;i++)
    {
        Pop(&s, &d);
        if (d.flag == 1)
            printf("%c", d.number.sign);
        else
            printf("%lf", d.number.num);
    }
}
棧的實現和基本操作實現
//四則運算需要的函數

Status MatchBrack(char* str);    //匹配括號是否正確

Status RotateStack(sqStack* s);    //將棧中的數據翻轉

Status GetMidStack(sqStack *s, char* str);    //獲取中綴表達式,將字符串轉換到棧中
Status GetBackStack(sqStack *s);    //獲取后綴表達式
Status GetBackValue(sqStack *s, double* val);    //獲取后綴表達式計算出來的結果
//括號匹配
Status MatchBrack(char* str)
{
    char *cur = str;
    sqStack BrkSK;
    data d;
    char ch;
    Status sta = OK;;

    if (!str || !InitStack(&BrkSK))
        return ERROR;

    while (*cur!='\0')
    {
        if (*cur=='('||*cur=='['||*cur=='{')
        {
            d.flag = 1;
            d.number.sign = *cur;
            Push(&BrkSK,d);
        }
        else if (*cur == ')' || *cur == ']' || *cur == '}' )
        {
            if (!GetTop(BrkSK, &d))
            {
                sta = ERROR;
                break;
            }
            ch = d.number.sign;
            if ((*cur == ')'&&ch == '(') || (*cur == ']'&&ch == '[') || (*cur == '}'&&ch == '{'))
                Pop(&BrkSK, &d);
            else
            {
                sta = ERROR;
                break;
            }
        }
        cur++;
    }
    DestroyStack(&BrkSK);
    return sta;
}

//獲取中綴表達式
Status GetMidStack(sqStack *s, char* str)
{
    char* cur, *start, *end;
    cur = start = end = str;
    char fnum[10] = { 0 };
    data d;

    if (!s || !str)
        return ERROR;

    while (*cur != '\0')
    {
        if (!isdigit(*cur) && *cur != '.')
        {
            if (cur > str&&isdigit(*(cur - 1)))
            {
                end = cur;
                memset(fnum, 0, 10);
                memcpy(fnum, start, (end - start));
                d.flag = 2;
                d.number.num = atof(fnum);
                Push(s, d);
            }
            d.flag = 1;
            d.number.sign = *cur;
            if (isdigit(*(cur + 1)))
                start = cur + 1;
            Push(s, d);
        }
        cur++;
    }
    if (start > end)    //最后以數字結尾,需要再進行判斷
    {
        end = cur;
        memset(fnum, 0, 10);
        memcpy(fnum, start, (end - start));
        d.flag = 2;
        d.number.num = atof(fnum);
        Push(s, d);
    }
    return OK;
}

//將棧中的數據翻轉
Status RotateStack(sqStack* s)
{
    sqStack tempSk, *freeSk;
    if (!InitStack(&tempSk) || !s)
        return ERROR;
    data d;
    int length = StackLength(*s);
    for (int i = 0; i < length; i++)
    {
        Pop(s, &d);
        Push(&tempSk, d);
    }
    freeSk = s;
    *s = tempSk;
    DestroyStack(freeSk);
}

//獲取后綴表達式
Status GetBackStack(sqStack *s)
{
    sqStack OperaSk, ResSk, *tempSk;
    OperaSk.base = ResSk.base = NULL;
    data d, top;
    char pc;    //存儲單個運算符
    int length = StackLength(*s);
    if (!s)
        return ERROR;
    if (!InitStack(&OperaSk) || !InitStack(&ResSk))
    {
        DestroyStack(&OperaSk);
        DestroyStack(&ResSk);
        return ERROR;
    }
    for (int i = 0; i < length; i++)
    {
        Pop(s, &d);
        if (d.flag == 2)
            Push(&ResSk, d);
        if (d.flag == 1)
        {
            if (GetTop(OperaSk, &top))
                pc = top.number.sign;

            if (d.number.sign == ')')
            {
                //將'('之上的運算符全部彈出到ResSk
                while (pc != '(')
                {
                    Pop(&OperaSk, &d);
                    Push(&ResSk, d);
                    GetTop(OperaSk, &top);
                    pc = top.number.sign;
                }
                Pop(&OperaSk, &d);    //將(一起彈出
                continue;
            }

            if (StackEmpty(OperaSk) || pc == '(' || d.number.sign == '(')    //若是OperaSk為空或者棧頂為(或者獲取的運算符為(,我們直接將這個運算符壓棧
                Push(&OperaSk, d);
            else if ((pc == '-' || pc == '+') && (d.number.sign == '*' || d.number.sign == '/'))    //若是棧頂的優先級低,也壓棧,但是要先將棧頂的
                Push(&OperaSk, d);
            else if (pc == d.number.sign || (pc == '-'&&d.number.sign == '+') || (pc == '+'&&d.number.sign == '-') || (pc == '*'&&d.number.sign == '/') || (pc == '/'&&d.number.sign == '*'))    //當棧頂優先級和當前運算符一致,先彈出到ResSk,在進行壓棧
            {
                Pop(&OperaSk, &top);
                Push(&ResSk, top);
                Push(&OperaSk, d);
            }
            else  //當獲取的運算符優先級低於棧頂優先級,先將棧頂運算符移棧到ResSk,然后再將當前運算符與下一次進行比較,壓棧到OperaSK棧
            {
                Pop(&OperaSk, &top);    //獲取OperaSK棧頂數據
                Push(&ResSk, top);        //將數據放入ResSK棧

                Push(s, d);    //由於當前數據沒有完成入棧,我們將它放回原來棧中,再次進行比較
                length++;
            }
        }
    }

    while (!StackEmpty(OperaSk))    //將運算符棧中的剩余的數據全部移到結果棧
    {
        Pop(&OperaSk, &d);
        Push(&ResSk, d);
    }

    tempSk = s;
    *s = ResSk;
    DestroyStack(tempSk);
    DestroyStack(&OperaSk);
    return OK;
}

//獲取后綴表達式計算出來的結果
Status GetBackValue(sqStack *s, double* val)
{
    sqStack OpValSk;    //獲取運算結果的棧
    int length = StackLength(*s);
    double op1, op2, value = 0;
    data d, top;

    if (!s || !InitStack(&OpValSk))
        return ERROR;

    for (int i = 0; i < length; i++)
    {
        Pop(s, &d);
        if (d.flag == 2)    //數字
            Push(&OpValSk, d);
        else   //運算符,取兩個數運算后,放回棧中
        {
            Pop(&OpValSk, &top);
            op2 = top.number.num;
            Pop(&OpValSk, &top);
            op1 = top.number.num;
            switch (d.number.sign)
            {
            case '+':
                value = op1 + op2;
                break;
            case '-':
                value = op1 - op2;
                break;
            case '*':
                value = op1 * op2;
                break;
            case '/':
                value = op1 / op2;
                break;
            default:
                break;
            }
            d.flag = 2;
            d.number.num = value;
            Push(&OpValSk, d);
        }
    }
    *val = value;
    DestroyStack(&OpValSk);
    return OK;
}
int main()
{
    sqStack sk;
    double value;
    ElemType e;
    char str[MAXSIZE] = { 0 };
    sk.base = sk.top = NULL;    //用於判斷是否存在
    //初始化空棧
    InitStack(&sk);
    //接收輸入的中綴表達式
    printf("please enter expression<no space>:");
    scanf("%s", str);
    //先進行括號匹配
    if (!MatchBrack(str))
        return 0;
    //將字符串中的中綴表達式分割,轉換為中綴表達式存放在棧中'1+2+3'--->3.0 + 2.0 + 1.0每一個都是一個棧空間數據
    GetMidStack(&sk, str);
    //將上面的中綴表達式翻轉,變為正常的
    RotateStack(&sk);
    //下面將中綴表達式轉后綴表達式
    GetBackStack(&sk);
    //將上面的后綴表達式翻轉,變為正常的
    RotateStack(&sk);
    //獲取后綴表達式結果
    GetBackValue(&sk, &value);
    //PrintStack(sk);
    printf("Operation values:%lf", value);
    //PrintStack(sk);
    DestroyStack(&sk);
    system("pause");
    return 0;
}

(五)結果演示

(六)反思

實現了小數運算,但是不足的是對方法不熟悉,導致耗費太多時間

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM