數據結構-棧


 

一、 棧

  1. 1.   為什么要學習棧?

棧是什么?為什么要學習它?現在先來說說棧的輝煌作用吧!在計算機領域中,棧是一種不可忽略的概念,無論從它的結構上,還是存儲數據方面,它對於學習數據結構的人們來說,都是非常重要的。那么就會有人問,棧究竟有什么作用,讓我們這么重視它?首先,棧具有非常強大的“記憶”功能,它可以保存對你有作用的數據,也可以被叫做保存現場;其次,當咱們調用一個帶參函數時候, 被調用的函數的形參,在編譯器編譯的時候,這些形參都需要一定的空間存放他們,這時計算機就會默認幫你保存到棧中了!

  1. 2.   棧的定義

 棧的作用,這是一個咱們生活中處處用到,但是卻又沒發現的一種現象,例如當你拿個籃子去買蘋果,那么你最先挑選的蘋果就是在籃子的最底下,最后挑選的蘋果就在籃子的最上邊,那么這就造成了這么一種現象:先拿進籃子的蘋果,要最后才能取出來;相反,最后拿進籃子的蘋果,就能最先取出來!

  棧是限定只能在表尾進行插入和刪除的線性表。

  我們把允許插入和刪除的一端稱作棧頂(Top),另一端稱作棧底(bottom)不含任何數據元素的棧被稱作空棧,棧也被稱為先進后出的線性表(具有線性關系)。

  而棧的特殊性,就是在表中想進行插入和刪除的操作,只能在棧頂進行。這也就使得了:棧底是非常穩定的,因為先進來的元素都被放在了棧底。

  棧的插入操作:叫做進棧,也叫作壓棧,入棧。

  棧的刪除操作:叫做出棧,也叫彈棧。

  1. 3.   進棧出棧變化形式

現在請大家思考這樣的一個問題:最先進棧的元素,是不是只能最后才能出來呢?

答案是不一定的,這個問題就要細分情況了。棧對線性表的插入和刪除的位置進行了限制,並沒有對元素的進出時間進行限制,這也就是說,在不是所有元素都進棧的情況下,事先進去的元素也可以先出站,只要確保一點:棧元素是從棧頂出棧就可以了!

舉例來說,現在有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先出棧的情況。

  上述的這個簡單例子,就可以看出來,只是3個棧元素,就有5種可能的出棧次序,如果元素量多,那么出棧的變化將會更多。

二、 棧的抽象數據類型

 對於棧來講,理論上線性表的操作特性它都具備,但是由於它的特殊性,所以針對它的操作上,也會有些變化。特別是插入和刪除的操作,我們改名為push和pop,英文翻譯為壓和彈!

  由於棧也是線性表,那么咱們之前講的線性表的順序存儲和鏈式存儲,對於棧來說,必然也是同樣適用的!

一、   ACM算法:順序棧的實現

既然棧是線性表的特例,那么棧的順序存儲其實也是線性表順序存儲的簡化,我們簡稱為順序棧。線性表是用數組來實現的,那么對於棧這種只能一頭進行插入和刪除的線性表來說,咱們一般也是用數組下標為0的一端,作為棧底。

  那么此時,我們定義一個StackSize表示數組的元素個數,top變量來指示棧頂元素在數組中的位置,那么就意味着top可以變大變小。如果此棧存在元素,那么top必然要小於StackSize,棧為空則top=-1,棧存在一個元素,top=0。

棧的結構定義:

typedef int SElemType; /* SElemType 類型根據實際情況而定,這里假設為int */

typedef struct

{

SElemType data[MAXSIZE];

int top; /* 棧頂指針 */

}SqStack;

  1. 1.   棧的順序存儲結構進棧操作

若現在有一個棧,StackSize是5,那么棧的普通情況、空棧、滿棧的情況分別如下:

對於棧的插入,即進棧操作,有如下所示:

因此對於進棧的操作push,其代碼如下:

/* 插入元素e為新的棧頂元素 */

Status Push(SqStack *S,SElemType e)

{

if(S->top == MAXSIZE -1 ) /* 滿棧 */

{

return ERROR;

}

S->top++; /* 棧頂指針增加一 */

S->data[s->top] = e; /* 將新元素e插入並賦值給棧頂空間 */

return OK;

}
  1. 1.   棧的順序存儲結構出棧操作

對於棧的刪除,即出棧操作,有如下代碼:

/* 先判斷棧是否為空,不空則刪除S的棧頂元素,用e返回其值,並返回OK,否則返回ERROR */

Statck Pop(SqStack *S, SElemType *e)

{

if(S->top == -1)

return ERROR;

*e = S->data[S->top]; /* 將要刪除的棧頂元素賦值給e */

S->top-- ;

return OK;

}

 

一、   棧的鏈式存儲結構

  1. 1.   棧出鏈式存儲結構

  棧的鏈式存儲結構,簡稱為鏈棧。

  鏈棧簡單的講,就是將棧和鏈表合二為一的使用,那么此時為了簡單,通常就把棧頂放在了鏈表的頭指針位置,此時棧頂指針就替代了頭指針,那么此時數據依次從棧頂進入,而且此時對於鏈表來說也就不再需要頭結點了。

  對於鏈棧來說,鏈棧的操作絕大部分都和單鏈表類似,而且基本上不存在棧滿的情況,除非內存已經沒有可以使用的空間。

  對於空棧來說,鏈表原定義是頭指針指向空,那么鏈棧的空其實就是top=NULL的時候。

對比一下順序棧與鏈棧,它們在時間復雜度上是一樣的,均為O(1)。對於空間性能,順序棧需要事先確定一個固定的長度,可能會存在內存空間浪費的問題,但它的優勢是存取時定位很方便,而鏈棧則要求每個元素都有指針域,這同時也增加了一些內存開銷,但對於棧的長度無限制。所以它們的區別和線性表中討論的一樣。如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那么最好是用鏈棧,反之,如果它的變化在可控的范圍內,建議使用順序棧會更好一些。

 棧的鏈表

/* Note:Your choice is C IDE */
#include "stdio.h"
#include "stdlib.h"
typedef struct person{
    int arr;
    struct person *next;//入棧壓棧push出棧彈棧pop
}PERSON;
PERSON *head;
void main()//棧頂是頭指針
{
    int e;
    int bh;
    PERSON *p,*pa,*pb;
    printf("1.入棧一個元素\n");
    printf("2.出棧一個元素\n");
    printf("3.打印\n");
    for(;;){
        printf("請輸入功能編號:");
    scanf("%d",&bh);
    switch(bh){
        case 1:
        printf("輸入元素:");
        scanf("%d",&e);
        p=(PERSON*)malloc(sizeof(PERSON));//分配一個空間
        
        p->arr=e;//將輸入的數據傳入鏈表中
        if(head==NULL){
            head=p;
            p->next=NULL;
        }else{
            p->next=head;
            head=p;
        }
        break;
        case 2:
        if(head==NULL)
        {
            printf("鏈表為空!\n");
            break;
            }
           pa=head;
           pb=head->next;
           head=head->next;
           printf("-%d-",pa->arr);
           free(pa);
        break;
        case 3:
        pa=head;
        printf("\n打印從棧頂到棧底的元素\n");
        while(pa){
            printf("--%d--",pa->arr);
            pa=pa->next;
        }
        printf("\n");
        break;
        }
    
    }
}

棧的順序表

#include "stdio.h"
#define MAX 5
typedef struct person{
    int arr[MAX];
    int top;
}PERSON;
PERSON p;

void main(void){
    int i,e;
    int bh;
    p.top=-1;
    printf("1.入棧一個元素\n");
    printf("2.出棧一個元素\n");
    printf("3.打印\n");
    for(;;){
    scanf("%d",&bh);
    switch(bh){
        case 1:
        if(p.top>=MAX-1){
            printf("\n棧已滿\n");
        }else{
        printf("輸入元素:");
        scanf("%d",&e);
        p.top++;
        p.arr[p.top]=e;
        }
        break;
        case 2:
        e=p.arr[p.top];
        printf("刪除的元素是%d\n",e);
        p.top--;
        break;
        case 3:
        printf("\n打印從棧頂到棧底的元素\n");
        for(i=p.top;i>=0;i--){
            printf("--%d--",p.arr[i]);
        }
        printf("\n");
        break;
        }
    
    }
}

 


免責聲明!

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



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