棧相關習題及詳解(選擇題和綜合題) ——數據結構

棧的基本概念

棧的定義

:只允許在一端進行插入或刪除操作的線性表。首先棧是一種線性表,但是限定這種線性表只能在某一端進行插入和刪除操作。

棧頂:線性表允許進行插入和刪除的那一端
棧底:固定的,不允許進行插入和刪除的另一端
空棧:不含任何元素的空表。

進棧出棧的變化形式

最先進棧的元素,是不是就只能是最後出棧呢

答案是不一定,要看什麼情況。棧對線性表的插入和刪除的位置進行了限制,並沒有對元素進出的時間進行限制,也就是說,在不是所有元素都進棧的情況下,進去的元素也可以出棧,只要保證是棧頂元素出棧就可以,那麼變化就很多了,舉例來說:

如果我們有3個整形數字元素1、2、3依次進棧,會有哪些出棧次序呢?

  • 第一種:1、2、3進,再3、2、1出。這事最簡單的最好理解的一種,出棧次序爲321
  • 第二種:1進,1出,2進,2出,3進,3出,出棧次序爲123
  • 第三種:1進,2進,2出,1出,3進,3出,出棧次序爲213
  • 第四種:1進,1出,2進,3進,3出,2出,出棧次序爲132
  • 第五種:1進,2進,2出,3進,3出,1出,出棧次序爲231

有沒有可能是312這樣的次序出棧呢,答案是不可能的,因爲3先出棧,就意味着3曾經進棧,既然3進棧了,那也意味着1和2已經進棧了,此時,2一定是在1的上面,就是更接近棧頂,那麼出棧只可能是321,不然不滿足123一次進棧的要求,所以此時不會發生1比2先出棧的情況

棧的基本操作

InitStack(&S):初始化一個空棧S
StackEmpty(S):判斷一個棧是否爲空,若棧S爲空返回true,否則返回false
Push(&S,x):進棧,若棧S未滿,將x加入使之成爲新棧頂
Pop(&S,&x):出棧,若棧S非空,彈出棧頂元素,並用x返回
GetTop(S,&x):讀棧頂元素,若棧S非空,用x返回棧頂元素
ClearStack(&S):銷燬棧,並釋放棧S佔用的存儲空間。

(符號“&”是C艹特有的,用來寶石引用,有的採用C語言中的指針類型“*”,也可以達到傳址的目的)

如果沒有特殊要求,可以直接用這些基本的操作函數

棧的順序存儲結構(順序棧)

順序棧的實現

棧的順序存儲稱爲順序棧,它是利用一組地址連續的存儲單元存放自棧底到棧頂的數據元素,同時附設一個指針(top)指示當前棧頂的位置。

順序棧可以描述爲:

#define MaxSize 50              //定義棧中元素的最大個數
typedef struct{
    Elemtype data[MaxSize];     //存放棧中元素
    int top;                    //棧頂指針
}SqStack;

共享棧

利用棧底位置相對不變的特性,可以讓兩個順序棧共享一個一位數據空間,將兩個棧的棧底分別設置在共享空間的兩端,兩個棧向共享空間的中間延伸。

兩個棧的棧頂指針都指向棧頂元素,top0=-1時0號棧爲空,top1=MaxSize時1號棧爲空,當且僅當兩個棧頂的指針相鄰時,判斷爲佔滿。當0號棧進棧時top0先加1再複製,1號棧進棧時top1先減1再複製;出棧時剛好相反。

共享棧是爲了更有效地利用存儲空間,兩個棧的空間相互調節,只有在整個存儲空間被佔滿時才發生上溢。其存取數據的時間複雜度均爲O(1)所以對存取效率沒有什麼影響。

事實上使用遮掩的數據結構,通常都是當兩個棧的空間需求有相反關係時,也就是一個棧增長時另一個棧在縮短的情況。就像買賣股票一樣,你買入時,一定是有一個你不知道的人在做賣出操作。有人賺錢,就一定有人賠錢。這樣使用兩棧共享空間存儲方法再有比較大的意義。

當然,這裏指兩個具有相同數據類型的棧,類型不同不適用這種方法。

棧的鏈式存儲結構(鏈棧)

採用鏈式存儲的棧成爲鏈棧,鏈棧的優點是便於多個棧共享存儲空間和提高效率,且不存在在棧滿上溢的情況。通常採用單鏈表實現,並規定所有操作都是在單鏈表的表頭進行的。這裏規定鏈棧沒有頭結點,Lhead指向棧頂元素。

棧的鏈式存儲類型可以描述爲

typedef struct Linknode{
    ElemType data;          //數據域
    struct Linknode *next;  //指針域
} *LinkStack;               //棧類型定義

習題部分

選擇題

第一題

棧和隊列具有相同的()
A. 抽象數據類型 B. 邏輯結構 C. 存儲結構 D. 運算

第二題

設鏈表不帶頭結點且所有操作均在表頭進行,則下列最不是和作爲鏈棧的是()
A. 只有表頭結點指針,沒有表尾指針的雙向循環鏈表
B. 只有表尾結點指針,沒有表頭指針的雙向循環鏈表
C. 只有表頭結點指針,沒有表尾指針的單向循環鏈表
D. 只有表尾結點指針,沒有表頭指針的單向循環鏈表

第三題

向一個棧頂指針爲top的鏈棧中插入一個x結點,則執行()
A. top->next=x B. x->next=top->next;top->next=x

C. x-next=top;top=x D. x->top->data;top=top->next

第四題

鏈棧執行pop操作,並將出棧的元素存在x中應該執行()
A. x=top;top=top->next B. x=top->data
C. top=top->next;x=top->data D. x=top->data;top=top->next

第五題

設棧S和隊列Q的初始狀態爲空,元素e1,e2,e3,e4,e5和e6一次通過棧S,一個元素出棧後即進隊列Q,若6個元素出棧的序列是e2,e4,e3,e6,e5,e1則棧的S的容量之多應該是()

A. 6 B. 4 C. 3 D. 2

第六題

若已知一個棧的入棧順序是1、2、3、4.其出棧序列爲P1、P2、P3、P4、,則P2、P4不可能是()

A. 2、4 B. 2、 1 C. 4 、 3 D. 3 、 4


解答部分

第一題

線性表、棧和隊列的邏輯結構都是相同的,都屬於線性結構,只是它們對數據的運算不同,從而表現出不同的特點

第二題

通常棧的插入和刪除在表頭進行。對於選項C,插入和刪除一個結點後,仍需要將其變爲循環單鏈表,因此需要找到其尾結點,時間複雜度爲O(n)。

若不做題幹中的限制,則棧頂可取表頭(帶頭結點的鏈表)或第二個結點(不帶頭結點的鏈表),棧指針的位置取投機誒單(帶頭結點的鏈表)或表頭(不帶頭結點的鏈表)

第三題

鏈棧採用不帶頭結點的單鏈表表示時,進棧操作在首部插入一個結點x(即x->next=top),插入完後需要將top指向該插入的結點x。如果帶頭結點呢?

第四題

這裏假設棧頂指針指向的是棧頂元素,所以選D;而A中首先將top指針賦給了x,錯誤;B中沒有修改top指針的值;C爲top指針指向棧頂元素的上一個元素時的答案。

第五題

本題將隊列和棧結合起來,由於隊列“先進先出”的特性,所以出隊的序列與進隊的序列是相通的,從而可以得到出棧的次序爲2,4,3,6,5,1;再由棧“先進後出”的特性,進棧的次序爲1,2,3,4,5,6,可見排在某個元素之後出棧卻在它會前進棧的元素個數,表示之前棧內元素個數。因此,4進棧後,1和3在棧中;而後4和3出棧;6進棧後,5和1也在棧中。因此,棧的最小容量是3。

第六題

對於A,可能的順序是1入棧,1出棧,2入棧,2出棧,3入棧,3出棧,4入棧,4出棧。
對於B可能的順序是1234入棧,4321出棧
D可能的順序是1入棧,1出棧,2入棧,3入棧,3出棧,2出棧,4入棧,4出棧。
而C卻沒有對應的序列。

綜合題

習題部分

第一題

假設以I和O 分別表示入棧和出棧操作。棧的初態和終態均爲空,入棧和出棧的操作序列可表示爲僅由I和O組成的序列,可以操作的序列稱爲合法序列,否則稱爲非法序列。

1)下面所示的序列中哪些是合法的?

A. IOIIOIOO B. IOOIOIIO C. IIIOIOIO D. IIIOOIOO

2)通過對1)的分析,寫出一個算法,判定所給的操作序列是否合法。若合法,返回true否則返回false(假定被判定的操作序列已存入一維數組中)

第二題

設單鏈表的表頭指針爲L,結點結構由data和next兩個域構成,其中data域爲字符型。試設計算法判斷該鏈表的全部n個字符是否中心對稱。例如xyx、xyyx都是中心對稱

第三題

設有兩個棧s1、s2都採用順序棧方式,並且共享一個存儲區[0,…,maxsize-1],爲了儘量利用空間,減少溢出的可能,可採用棧頂相向,迎面增長的存儲方式。試設計s1、s2有關入棧和出棧的操作算法

解答部分

第一題

1) AD
2)設被判定的操作序列已存入一維數組A中。

算法的基本設計思想:一次逐一掃描入棧出棧序列(即I和O組成的字符串)每掃描至任一位置均需檢查出戰次數(即O的個數)是否小於入棧次數(I的個數),若大於則爲非法序列。掃描結束後,再判斷入棧和出棧數是否相等,若不相等則不合題意,爲非法序列。

int Judge(char A[]){
    //判斷字符數組A中的輸入輸出序列是否是和發序列。如果是返回true,否則返回false
    int i =0;
    int j=k=0;                          //i爲下標,j和k分別爲字母I和O的個數
    while(A[i]!='0'){                   //未到字符數組尾
        switch(A[i]){
            case 'I':j++ ;break;        //入棧次數增1
            case 'O';k++;
            if (k>j){printf("序列非法\n", ); exit(0);}
        }
        i++;                            //不論A[i]是‘I’或‘O’,指針i均後移
    }                                   //while
    if(j!=k){
        printf("序列非法\n");
        return false;
    }
    else{
        printf("序列合法\n");
        return true;
    }
}

另解:入棧後,棧內元素個數加1;出棧後,棧內元素個數減1,因此,可以將判定一組出入棧序列是否合法,轉化爲一組+1、-1組成的序列,它的任一前綴子序列的累加和不小於0(每次出棧或入棧操作後判斷),則合法;否則,非法

第二題

算法思想:使用棧來判斷鏈表中的數據是否中心對稱。將鏈表的前一半元素一次進棧。在處理鏈表的後一半元素時,當訪問到鏈表的一個元素後,就從棧中彈出一一個元素,兩個元素比較,若相等,則將鏈表中下一個元素與棧中再彈出的元素比較,直至鏈表到尾。這時若棧是空棧,則得出鏈表中心對稱的結論;否則,當鏈表中的一個元素與棧中彈出元素不等時,結論爲鏈表非中心對稱,結束算法的執行

int dc(LinkList L,int n){
    //L是帶頭結點的n個元素單鏈表,本算法判斷你鏈表是否失中心對稱
    int i;
    char s[n/2];
    p=L->next;                      //p是鏈表的工作指針,指向待處理的當前元素
    for(i=0;i<n/2;i++){             //鏈表一半元素進棧
        s[i]=p->data;
        p=p->next;
    }
    i--;                            //回覆最後的i值
    if(n%2==1)                      //若n是技術,後移過中心節點
        p=p->next;
    while(p!=NULL&&s[i]==p->data){  //檢測是否中心對稱
        i--;                        //i充當棧頂指針
        p=p->next;
    }
    if(i==-1)                       //棧爲空棧
        return 1;                   //鏈表中心對稱
    else
        return 0;                   //鏈表不中心對稱
}

算法先將“鏈表的前一半”元素(字符)進棧。當n爲偶數時,前一半和後一半的個數相同;當n爲奇數時,倆你報中心節點字符不必計較,移動鏈表指針到下一個字符開始比較。比較過程中遇到不相等時,立即退出while循環,不再進行比較。

第三題

兩個棧共享向量空間,將兩個棧的棧底設在向量兩段,初始時,s1棧頂指針爲-1,s2棧頂指針爲maxsize。兩個棧頂指針相鄰時爲棧滿。兩個棧頂相向、迎面增長,棧頂指針指向棧頂元素。

#define maxsize 100             //兩個棧共享順序存儲空間所能達到的最多元素數初始化爲100

#define elemtp int              //假設元素類型爲整型
typedef struct{                 
    elemtp stack[maxsize]       //棧空間
    int top[2];                 //top爲兩個棧頂指針
}stk;

本題的關鍵在於,兩個棧入棧和退棧時的棧頂指針的計算。s1棧時通常意義下的棧;而s2棧入棧操作時,其棧頂指針左移(-1),退棧時,棧頂指針右移(+1)

此外,對於所有棧的操作,都要注意“入棧判滿,出棧判空”的檢查

(1)

int push(int i ,elemtp x){
    //入棧操作,i爲棧號,i=0表示左邊的s1棧,i=1表示右邊的s2棧,x是入棧元素
    //入棧成功返回1,否則返回0

    if(i<0||i>1){
        printf("棧號輸入不對\n");
        exit(0);
    }
    if(s.top[1]-s.top[0]==1){
        printf("棧已滿\n");
        return 0;
    }
    switch(i){
        case 0:s.stack[++s.top[0]]=x;return 1;break
        case 1:s.stack[--s.top[1]]=x;return 1;
    }
}

(2)


elemtp pop(int i ){
    //退棧算法。i表示棧號,i=0時爲s1棧,i=1時爲s2棧
    //退棧成功返回退棧元素,否則返回-1
    if(i<0||i>1){
        printf("棧號輸入不對\n");
        exit(0);

    }
    switch(i){
        case 0:
            if(s.top[0]==-1){
                printf("棧空\n");
                return -1;
            }
            else
                return s.stack[s.top[0]--];
        case 1:
            if(s.top[1]==maxsize){
                printf("棧空\n");
                return -1;
            }
            else 
                return s.stack[s.top[1]++];

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