1. 程式人生 > >【資料結構與演算法】 棧——棧的應用舉例(3例)

【資料結構與演算法】 棧——棧的應用舉例(3例)

根據“後進先出”特性,生活中的例子比比皆是。

1數制轉換

假設要講十進位制轉換為二進位制。以 52 為例,如圖:


在上述計算過程中,第一次求出的X值為最低位,最後一次求出的X值為最高位。而列印時應從高位到低位進行,恰好與計算過程相反。根據這個特點,我們可以通過入棧出棧來實現,即將計算過程中依次得到的二進位制數碼按順序進棧,計算結束後,再順序出棧,並按出棧順序列印輸出。即可得到給定的二進位制數。這是利用棧後進先出特性的最簡單例子。

void Conversion(int N){
    /*對於任意的一個非負十進位制數N,打印出與其對應的二進位制數*/
    Stack S;
    int x;
    InitStack(&S);
    while (N>0) {
        x=N%2;
        Push(&S,x); //將餘數壓入棧S
        N=N/2;
    }
    while (!IsEmpty(&S)) {
        Pop(&S,&X);
        printf("%d",x);
    }
}

2括號匹配問題

假設表示式有三種括號:圓括號“()”,花括號“{}”,方括號“[]”。它們可互相巢狀,如{([])}或{([])()[]}均為正確格式。而{)),{[()]均為不正確格式。如何檢測其正確性?可以應用棧。

我們以“{[()]}”為例,依次讀取括號,如圖:


注意,上面寫的雙雙出棧其實是,左括號出棧。

void BrackerMatch(char *str){
    Stack S;
    int i;
    char ch;
    InitStack(&S);
    for (i=0;str[i]!='/0';i++)
    {
        switch (str[i]) {
            case '(':
            case '[':
            case '{':
                Push(&S,str[i]);
                break;
            case ']':
            case '}':
            case ')':
                if (IsEmpty(&S)) {
                    printf("\n 右括號多餘!");
                    return;
                }else{
                    GetTop(&S,&ch);
                    if(Match(ch,str[i])) //用 Match 判斷兩個括號是否匹配
                        Pop(&S,&ch);     //已匹配的左括號出棧
                    else{
                        printf("\n對應的左右括號不同類");
                        return;
                    }
                }
            default:
                break;
        }
        if (IsEmpty(&S))
            printf("\n括號匹配!");
        else
            printf("\n左括號多餘!");
    }
}

3表示式求值

表示式求值是高階語言編譯中的一個基本問題,是棧的典型應用例項。

1+2*3/(1+5)=2

任何一個表示式都是由數(運算元)和符號(運算子+-*/,界限符())組成。

而符號又有優先順序,優先計算得到的值再作為數繼續運算,當符號用盡後,值即為所得。

1+2*3/(1+5)   -->   1+6/(1+5)   -->   1+6/6   -->   1+1   -->   2

優先順序規則:

(1)先左後右

(2)先乘除,後加減

(3)先括號內,後括號外

此時需要兩個棧,一個稱作operator,用以存放符號。一個稱作operand,用於存放運算元或中間結果。下圖,為即將要用到的算符之間的優先關係。


演算法的基本過程如下:

初始化兩個棧,然後講表示式起始符“#”壓入operator棧。

輸入表示式,以“#”結尾。

依次讀取表示式,讀取過程中,若是運算元,直接入棧operand。若是符號,則與operator棧頂運算子進行比較。

(1)若 棧頂符<剛讀運算子 : 剛讀運算子 進棧;

(2)若 棧頂符>剛讀運算子 : 進行計算,operator棧頂符退棧,送入x,同時將operand棧退棧兩次,得到a,b,對a,b,x進行運算後,將結果作為中間結果推入operand棧;

(3)若 棧頂符=剛讀運算子 : 說明是左右括號相遇,棧頂符退棧即可;

當operator棧的棧頂符和當前符都為“#”時,說明求值完畢。

以(10+2)*3# 為例,如圖:


當棧頂符和當前符都是“#”,計算結束

int ExpEvaluation(){
    /*operator為符棧,operand為數棧,OPS為運算子集合*/
    InitStack(&operand);
    InitStack(&operator);
    Push(&operator,'#');
    printf("\n輸入一個表示式(以#結束)");
    ch=getchar();
    while(ch!='#'||GetTop(operator)!='#'){ //GetTop 取棧頂
        if (!In(ch,OPS)) {                  //不是操作符,是運算元
            int temp;
            temp=ch-'0';                //數的臨時變數,並轉換為十進位制
            ch=getchar();
            while (!In(ch,OPS)) {        //數可能不只有一位,如100就要讀三次
                temp=temp+10+ch-'0';
                ch=getchar();
            }
            Push(&operand,temp);
        }
        else
            switch (Compare(GetTop(operator),ch)) {
                case '<':
                    Push(&operator,ch);
                    ch=getchar();
                    break;
                case '=':
                    Pop(&operator,&x);
                    ch=getchar();
                    break;
                case '>':
                    Pop(&operator,&op);
                    Pop(&operand,&b);
                    Pop(&operand,&a);
                    v=Execute(a,op,b);   //對a和b進行op運算
                    Push(&operand,v);    //注意這裡沒有 ch=getchar()
                    break;
            }
    }
    v=GetTop(&operand);
    return v;
}


以上均為學習耿國華的《資料結構-c語言》筆記