數據結構(四):棧

一、棧的定義

棧是只能在一端進行插入和刪除操作的線性表。表中允許進行插入、刪除操作的一端稱爲棧頂。棧頂的位置是動態的,由一個稱爲棧頂指針的位置指示器指示。表的另一端稱爲棧底。

當棧中沒有元素時,稱爲空棧。棧的插入操作通常稱爲入棧或者進棧,棧的刪除操作一般稱作出棧或者退棧。

棧的主要特點是 ”後進先出“ ,即後進棧的元素先出棧。每次進棧的元素都放在原來棧頂元素之前,成爲新的棧頂元素,每次出棧的元素都是當前棧頂元素。所以棧也被稱爲後進先出表。

棧的抽象數據類型定義如下:

ADT List
{
    數據對象:
        D={a(i)|1=<i<=n, a(i)是 ElemType類型}
    數據關係:
        R={<a(i), a(i+1)>|a(i),a(i+1)屬於 D, i=1,2,3,···,n-1}
    基本運算:
        InitStack(&s): 初始化棧,構建一個空棧 s
        ClearStack(&s): 銷燬棧:釋放棧 s所佔的內存空間
        StackLength(s): 求棧的長度: 返回棧 s中的元素個數
        StackEmpty(s): 判斷棧是否爲空:若棧 s爲空則返回真,否則返回假
        Push(&s, e): 進棧:將元素 e添加到棧頂
        Pop(&s, &e): 出棧:將棧頂元素賦值給 e並從棧頂刪除
        GetTop(s, &e): 取棧頂元素:將當前棧頂元素的值賦值給 e
        DispStack(s):顯示棧中所有元素的值:按照從棧頂到棧底的順序顯示棧中所有元素的值
}

二、棧的順序存儲結構與基本運算

使用順序存儲結構存儲棧時,我們會分配一塊連續的內存空間來存放棧中的元素,並用一個變量指向當前的棧頂。採用順序存儲結構的棧稱爲順序棧。

定義順序棧的結合體時,我們假設棧能夠存儲的最大元素數量爲 MaxSize,指向棧頂的變量爲 STop(在順序存儲結構中 STop是一個整數,表示當前棧頂元素的下標),存放棧中元素的數組 data。

所以我們可以定義棧的結構體爲:

#define MaxSize 100

typedef char ElemType;
typedef struct Stack{
    int STop;
    ElemType data[MaxSize];
}Stack;

這裏棧的最大大小我們通過宏來定義,在實現時也可以使用其他的方法定義,比如在棧中再加一個 MaxSize,data改爲一個 ElemType[] 類型的指針,在初始化棧時,通過 malloc函數讀取 MaxSize來爲 data分配內存。

1、棧的初始化

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

在初始化函數裏我我們把 s->STop賦值爲 -1,以此來標記棧 s是空棧。

也可以把 STop賦值爲 0,這與 -1區別不大,只是當初始化爲 -1時 STop總是指向棧頂元素,而初始化爲 0時 STop總是指向棧頂元素的前一位。

2、銷燬棧

void ClearStack(Stack* &s) {
    free(s);
    s = NULL;
}//ClearStack

3、求棧的長度

int StackLength(Stack* s) {
    return s->STop + 1;
}//StackLengt

這裏我們返回 STop+1是因爲 STop總是指向棧頂元素,而棧頂元素的下標就是數組元素個數減一。

當 STop初始化爲 0時我們就可以直接返回 STop。

4、判斷棧是否爲空

bool StackEmpty(Stack* s) {
    if (s->STop < 0) {
        return true;
    }
    else{
        return false;
    }
}//StackEmpty

5、進棧

void Push(Stack* &s, ElemType e) {
    s->STop++;
    
    if (s->STop > MaxSize || s->STop < 0) {
        throw;
    }
    else{
        s->data[s->STop] = e;
    }
}//Push

6、出棧

void Pop(Stack* &s, ElemType &e) {
    if (!StackEmpty(s)) {
        e = s->data[s->STop];
        s->STop--;
    }
    else {
        throw;
    }
}//Pop

7、取棧頂元素

void GetTop(Stack* s, ElemType &e) {
    if (!StackEmpty(s)) {
        e = s->data[s->STop];
    }
    else {
        throw;
    }
}//GetTop

8、打印棧

void DispStack(Stack* s) {
    if (StackEmpty(s)) {
        printf("stack is empty.\n");
    }
    else {
        int top = s->STop;
        while (top >= 0) {
            printf("index: %d, value: %c\n", top, s->data[top]);
            top--;
        }
    }
}//DispStack

三、棧的鏈式存儲結構與基本運算

棧的鏈式存儲結構叫做鏈棧,通常用單鏈表實現。鏈棧的有點是不用考慮滿棧的情況,操作也很方便,我們只要在頭節點處操作就行。最靠近頭節點的節點是棧頂節點,而鏈表中與頭節點相對的一端則是棧底。

鏈棧中節點的結構定義如下:

typedef char ElemType;
typedef struct LinkStack {
    ElemType data;
    struct LinkStack* next;
}LinkStack;

1、鏈棧的初始化

void InitStack(LinkStack* &s) {
    s = (LinkStack*)malloc(sizeof(LinkStack));
    s->next = NULL;
}

2、銷燬鏈棧

void ClearStack(LinkStack* &s) {
    while(s->next != NULL) {
        LinkStack* temp = s->next->next;
        free(s->next);
        s->next = temp;
    }
    free(s);
    s = NULL;
}

3、求鏈棧的長度

int StackLength(LinkStack* s) {
    int i = 0;
    while (s->next != NULL) {
        i++;
        s->next = s->next->next;
    }
    return i;
}

4、判斷鏈棧是否爲空

bool StackEmpty(LinkStack* s) {
    return s->next == NULL;
}

5、進棧

void Push(LinkStack* &s, ElemType e) {
    LinkStack* new_node = (LinkStack*)malloc(sizeof(LinkStack));
    if (new_node == NULL) {
        throw;
    }

    new_node->data = e;
    new_node->next = s->next;
    s->next = new_node;
}

鏈棧進棧與單鏈表添加節點很相似,只是鏈棧進棧時我們只在頭節點後面添加節點,而單鏈表則可以在鏈表的任意位置添加節點。

6、出棧

void Pop(LinkStack* &s, ElemType &e) {
    if (StackEmpty(s)) {
        throw;
    }

    LinkStack* temp = s->next;
    e = temp->data;
    s->next = temp->next;
    free(temp);
}

7、取棧頂元素

void GetTop(LinkStack* s, ElemType &e) {
    if (StackEmpty(s)) {
        throw;
    }

    e = s->next->data;
}

8、打印棧

void DispLStack(LinkStack* s) {
    LinkStack* temp = (LinkStack*)malloc(sizeof(LinkStack));
    *temp = *s;

    int i = 0;
    while (temp->next != NULL) {
        i++;
        printf("index: %d, value: %c\n", i, temp->next->data);
        temp->next = temp->next->next;
    }
}

這裏使用一個 temp作爲中間變量是爲了避免直接修改 s所指向的內存。

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