數據結構學習總結 棧和隊列


一,順序棧的基本操作
            同 順序表鏈表一樣,棧也是用來存儲邏輯關系為 "一對一" 數據的線性存儲結構,如  1 所示。
          
                      棧存儲結構示意圖
                                                              圖 1 棧存儲結構示意圖
  從圖 1 我們看到,棧存儲結構與之前所學的線性存儲結構有所差異,這緣於棧對數據 "存" 和 "取" 的過程有特殊的要求:
  1,棧只能從表的一端存取數據,另一端是封閉的,如圖 1 所示;

  2,在棧中,無論是存數據還是取數據,都必須遵循"先進后出"的原則,即最先進棧的元素最后出棧。拿圖 1 的棧來說,從圖中數據的存儲狀態可判斷出,元素 1 是最先進的棧。因此,當需要從棧 中取出元素 1 時,根據"先進后出"的原則,需提前將元素 3 和元素 2 從棧中取出,然后才能成功取出元素 1。

     因此,我們可以給棧下一個定義,即棧是一種只能從表的一端存取數據且遵循 "先進后出" 原則的線性存儲結構。
   通常,棧的開口端被稱為 棧頂,封口端被稱為 棧底
因此,棧頂元素指的就是距離棧頂最近的元素,拿圖 2 來說,棧頂元素為元素 4;同理,棧底元素指的是位於棧最底部的元素,圖 2 中的棧底元素為元素 1。
    
                       棧頂和棧底
                        圖 2 棧頂和棧底

進棧和出棧

         基於 棧結構的特點,在實際應用中,通常只會對棧執行以下兩種操作:
  1. 向棧中添加元素,此過程被稱為"進棧"(入棧或壓棧);
  2. 從棧中提取出指定元素,此過程被稱為"出棧"(或彈棧);

棧的具體實現

        棧是一種 "特殊" 的線性存儲結構,因此棧的具體實現有以下兩種方式:

  1. 順序棧:采用順序存儲結構可以模擬棧存儲數據的特點,從而實現棧存儲結構;
  2. 鏈棧:采用鏈式存儲結構實現棧結構;
 兩種實現方式的區別,僅限於數據元素在實際物理空間上存放的相對位置,順序棧底層采用的是 數組,鏈棧底層采用的是鏈表。有關順序棧和鏈棧的具體實現會在后續章節中作詳細講解。

棧的應用

基於棧結構對數據存取采用 "先進后出" 原則的特點,它可以用於實現很多功能。

例如,我們經常使用瀏覽器在各種網站上查找信息。假設先瀏覽的頁面 A,然后關閉了頁面 A 跳轉到頁面 B,隨后又關閉頁面 B 跳轉到了頁面 C。而此時,我們如果想重新回到頁面 A,有兩個選擇:
  • 重新搜索找到頁面 A;
  • 使用瀏覽器的"回退"功能。瀏覽器會先回退到頁面 B,而后再回退到頁面 A。
  瀏覽器 "回退" 功能的實現,底層使用的就是棧存儲結構。當你關閉頁面 A 時,瀏覽器會將頁面 A 入棧;同樣,當你關閉頁面 B 時,瀏覽器也會將 B入棧。因此,當你執行回退操作時,才會首先看到的是頁面 B,然后是頁面 A,這是棧中數據依次出棧的效果。

不僅如此,棧存儲結構還可以幫我們檢測代碼中的 括號匹配問題。多數編程語言都會用到括號(小括號、中括號和大括號),括號的錯誤使用(通常是丟右括號)會導致程序編譯錯誤,而很多開發工具中都有檢測代碼是否有編輯錯誤的功能,其中就包含檢測代碼中的括號匹配問題,此功能的底層實現使用的就是棧結構。

同時,棧結構還可以實現數值的 進制轉換功能。例如,編寫程序實現從十進制數自動轉換成二進制數,就可以使用棧存儲結構來實現。

以上也僅是棧應用領域的冰山一角,這里不再過多舉例。在后續章節的學習中,我們會大量使用到棧結構。
接下來,我們學習如何實現順序棧和鏈棧,以及對棧中元素進行入棧和出棧的操作。
      順序 ,即用 順序表實現棧存儲結構。通過前面的學習我們知道,使用棧存儲結構操作數據元素必須遵守 "先進后出" 的原則,本節就 "如何使用順序表模擬棧以及實現對棧中數據的基本操作(出棧和入棧)" 給大家做詳細介紹。
      如果你仔細觀察順序表(底層實現是 數組)和棧結構就會發現,它們存儲數據的方式高度相似,只不過棧對數據的存取過程有特殊的限制,而順序表沒有。
 例如,我們先使用順序表(a 數組)存儲  {1,2,3,4},存儲狀態如  1 所示:
              順序表存儲 {1,2,3,4}
                  圖 1 順序表存儲 {1,2,3,4}
 同樣,使用棧存儲結構存儲  {1,2,3,4},其存儲狀態如圖 2 所示:
                   棧結構存儲 {1,2,3,4}
                 圖 2 棧結構存儲 {1,2,3,4}
 通過圖 1 和圖 2 的對比不難看出,使用順序表模擬棧結構很簡單,只需要將數據從 a 數組下標為 0 的位置依次存儲即可。
 從數組下標為 0 的模擬棧存儲數據是常用的方法,從其他數組下標處存儲數據也完全可以,這里只是為了方便初學者理解。
 了解了順序表模擬棧存儲數據后,接下來看如何模擬棧中元素出棧的操作。由於棧對存儲元素出棧的次序有"先進后出"的要求,如果想將圖 1 中存儲的元素 1 從棧中取出,需先將元素 4、元素 3 和元素 2 依次從棧中取出。
    這里給出使用順序表模擬棧存儲結構常用的實現思路,即在順序表中設定一個實時指向棧頂元素的變量(一般命名為 top),top 初始值為 -1,表示棧中沒有存儲任何數據元素,及棧是"空棧"。一旦有數據元素進棧,則 top 就做 +1 操作;反之,如果數據元素出棧,top 就做 -1 操作

順序棧元素"入棧"

       比如,還是模擬棧存儲  {1,2,3,4} 的過程。最初,棧是"空棧",即數組是空的,top 值為初始值 -1,如圖 3 所示:

           空棧示意圖
                    圖 3 空棧示意圖
 首先向棧中添加元素 1,我們默認數組下標為 0 一端表示棧底,因此,元素 1 被存儲在數組 a[1] 處,同時 top 值 +1,如圖 4 所示:
            模擬棧存儲元素 1
                圖 4 模擬棧存儲元素 1
 
      采用以上的方式,依次存儲元素 2、3 和 4,最終,top 值變為 3,如圖 5 所示:

                模擬棧存儲{1,2,3,4}
                圖 5 模擬棧存儲{1,2,3,4}

 因此,C 語言實現代碼為:   

//元素elem進棧,a為數組,top值為當前棧的棧頂位置
int push(int* a,int top,int elem){
    a[++top]=elem;
    return top;
}

代碼中的 a[++top]=elem,等價於先執行 ++top,再執行 a[top]=elem。

順序棧元素"出棧"

其實,top 變量的設置對模擬數據的 "入棧" 操作沒有實際的幫助,它是為實現數據的 "出棧" 操作做准備的。

比如,將圖 5 中的元素 2 出棧,則需要先將元素 4 和元素 3 依次出棧。需要注意的是,當有數據出棧時,要將 top 做 -1 操作。因此,元素 4 和元素 3 出棧的過程分別如圖 6a) 和 6b) 所示:
 
             數據元素出棧
                圖 6 數據元素出棧
 注意,圖 6 數組中元素的消失僅是為了方便初學者學習,其實,這里只需要對 top 值做 -1 操作即可,因為 top 值本身就表示棧的棧頂位置,因此 top-1 就等同於棧頂元素出棧。並且后期向棧中添加元素時,新元素會存儲在類似元素 4 這樣的舊元素位置上,將舊元素覆蓋。
 元素 4 和元素 3 全部出棧后,元素 2 才能出棧。因此,使用順序表模擬數據出棧操作的 C 語言實現代碼為
//數據元素出棧
int pop(int * a,int top){
    if (top==-1) {
        printf("空棧");
        return -1;
    }
    printf("彈棧元素:%d\n",a[top]);
    top--;
    return top;
}

代碼中的 if 語句是為了防止用戶做 "棧中已無數據卻還要數據出棧" 的錯誤操作。代碼中,關於對棧中元素出棧操作的實現,只需要 top 值 -1 即可。

總結

通過學習順序表模擬棧中數據入棧和出棧的操作,初學者完成了對順序棧的學習,這里給出順序棧及對數據基本操作的 C 語言完整代碼:
#include <stdio.h>
//元素elem進棧
int push(int* a,int top,int elem){
    a[++top]=elem;
    return top;
}
//數據元素出棧
int pop(int * a,int top){
    if (top==-1) {
        printf("空棧");
        return -1;
    }
    printf("彈棧元素:%d\n",a[top]);
    top--;
    return top;
}
int main() {
    int a[100];
    int top=-1;
    top=push(a, top, 1);
    top=push(a, top, 2);
    top=push(a, top, 3);
    top=push(a, top, 4);
    top=pop(a, top);
    top=pop(a, top);
    top=pop(a, top);
    top=pop(a, top);
    top=pop(a, top);
    return 0;
}

程序輸出結果為:

  

,即用 鏈表實現棧存儲結構。

鏈棧的實現思路同順序棧類似,順序棧是將數順序表數組)的一端作為棧底,另一端為棧頂;鏈棧也如此,通常我們將鏈表的頭部作為棧頂,尾部作為棧底,如 1 所示:


           鏈棧示意圖
                      圖 1 鏈棧示意圖
 將鏈表頭部作為棧頂的一端,可以避免在實現數據 "入棧" 和 "出棧" 操作時做大量遍歷鏈表的耗時操作。
 鏈表的頭部作為棧頂,意味着:
  • 在實現數據"入棧"操作時,需要將數據從鏈表的頭部插入;
  • 在實現數據"出棧"操作時,需要刪除鏈表頭部的首元節點;
因此,鏈棧實際上就是一個只能采用頭插法插入或刪除數據的鏈表。

鏈棧元素入棧

例如,將元素 1、2、3、4 依次入棧,等價於將各元素采用頭插法依次添加到鏈表中,每個數據元素的添加過程如圖 2 所示:
              鏈棧元素依次入棧過程示意圖
            圖 2 鏈棧元素依次入棧過程示意圖
C語言實現代碼為:
//鏈表中節點結構
typedef struct lineStack{
    int data;
    struct lineStack* next;
};
//壓棧 stack 當前鏈棧  a 入棧元素
lineStack* push(lineStack* stack,int a) {
     //創建存儲新元素的節點
    lineStack* temp = (lineStack*)malloc(sizeof(lineStack));
    temp->data = a;
    //新節點與頭節點建立關聯
    temp->next = stack;
    //更新頭指針指向
    stack = temp;
    return stack;
}

鏈棧元素出棧

例如,圖 2e) 所示的鏈棧中,若要將元素 3 出棧,根據"先進后出"的原則,要先將元素 4 出棧,也就是從鏈表中摘除,然后元素 3 才能出棧,整個操作過程如圖 3 所示:


       鏈棧元素出棧示意圖
            圖 3 鏈棧元素出棧示意圖
因此,實現棧頂元素出鏈棧的 C 語言實現代碼為:
//棧頂元素出棧的實現函數
lineStack* pop(lineStack* stack) {
    if (stack) {
      //聲明一個新指針指向棧頂節點
        lineStack *p = stack;
        //更新頭節點
        stack = stack->next;
        printf("出棧元素:%d \n",p->data);
        if (stack) {
            printf("新棧頂元素:%d\n",stack->data);
        }
        else {
            printf("棧已空\n");
        }
        free(p);
    }
    else {
        printf("棧內沒有元素\n");
        return stack;
    }
    return stack;

代碼中通過使用 if 判斷語句,避免了用戶執行"棧已空卻還要數據出棧"錯誤操作。

總結

本節,通過采用頭插法操作數據的單鏈表實現了鏈棧結構,這里給出鏈棧及基本操作的C語言完整代碼:
#include<stdlib.h>
#include<stdio.h>
//鏈表中節點結構
typedef struct lineStack{
    int data;
    struct lineStack* next;
};
//壓棧 stack 當前鏈棧  a 入棧元素
lineStack* push(lineStack* stack,int a) {
     //創建存儲新元素的節點
    lineStack* temp = (lineStack*)malloc(sizeof(lineStack));
    temp->data = a;
    //新節點與頭節點建立關聯
    temp->next = stack;
    //更新頭指針指向
    stack = temp;
    return stack;
}

//棧頂元素出棧的實現函數
lineStack* pop(lineStack* stack) {
    if (stack) {
      //聲明一個新指針指向棧頂節點
        lineStack *p = stack;
        //更新頭節點
        stack = stack->next;
        printf("出棧元素:%d ",p->data);
        if (stack) {
            printf("新棧頂元素:%d\n",stack->data);
        }
        else {
            printf("棧已空\n");
        }
        free(p);
    }
    else {
        printf("棧內沒有元素\n");
        return stack;
    }
    return stack;
}
int main() {
    lineStack * stack = NULL;
    stack = push(stack, 1);
    stack = push(stack, 2);
    stack = push(stack, 3);
    stack = push(stack, 4);
    stack = pop(stack);
    stack = pop(stack);
    stack = pop(stack);
    stack = pop(stack);
    stack = pop(stack);
    return 0;
}
View Code

運行結果:

 二,順序表及隊列的實現

         隊列,和 一樣,也是一種對數據的"存"和"取"有嚴格要求的線性存儲結構。
  與棧結構不同的是,隊列的兩端都"開口",要求數據只能從一端進,從另一端出,如  1 所示:
 
             隊列存儲結構
                  圖 1 隊列存儲結構
通常,稱進數據的一端為 "隊尾",出數據的一端為 "隊頭",數據元素進隊列的過程稱為 "入隊",出隊列的過程稱為 "出隊"。
 不僅如此,隊列中數據的進出要遵循 "先進先出" 的原則,即最先進隊列的數據元素,同樣要最先出隊列。拿圖 1 中的隊列來說,從數據在隊列中的存儲狀態可以分析出,元素 1 最先進隊,其次是元素 2,最后是元素 3。此時如果將元素 3 出隊,根據隊列 "先進先出" 的特點,元素 1 要先出隊列,元素 2 再出隊列,最后才輪到元素 3 出隊列。
 棧和隊列不要混淆,棧結構是一端封口,特點是"先進后出";而隊列的兩端全是開口,特點是"先進先出"。
 因此,數據從表的一端進,從另一端出,且遵循 "先進先出" 原則的線性存儲結構就是隊列。

隊列的實現

 隊列存儲結構的實現有以下兩種方式:
  1. 順序隊列:在順序表的基礎上實現的隊列結構;
  2. 鏈隊列:在鏈表的基礎上實現的隊列結構;
 兩者的區別僅是順序表和鏈表的區別,即在實際的物理空間中,數據集中存儲的隊列是順序隊列,分散存儲的隊列是鏈隊列。
 
隊列的實際應用
實際生活中,隊列的應用隨處可見,比如排隊買 XXX、醫院的掛號系統等,采用的都是隊列的結構。

拿排隊買票來說,所有的人排成一隊,先到者排的就靠前,后到者只能從隊尾排隊等待,隊中的每個人都必須等到自己前面的所有人全部買票成功並從隊頭出隊后,才輪到自己買票。這就不是典型的隊列結構嗎?
明白了什么是隊列,接下來開始系統地學習順序隊列和鏈隊列。
順序 隊列,即采用 順序表模擬實現的隊列結構。
     我們知道,隊列具有以下兩個特點:
  1. 數據從隊列的一端進,另一端出;
  2. 數據的入隊和出隊遵循"先進先出"的原則;
因此,只要使用順序表按以上兩個要求操作數據,即可實現順序隊列。首先來學習一種最簡單的實現方法。

順序隊列簡單實現

由於順序隊列的底層使用的是 數組,因此需預先申請一塊足夠大的內存空間初始化順序隊列。除此之外,為了滿足順序隊列中數據從隊尾進,隊頭出且先進先出的要求,我們還需要定義兩個指針(top 和 rear)分別用於指向順序隊列中的隊頭元素和隊尾元素,如  1 所示:
                       順序隊列實現示意圖
                          圖 1 順序隊列實現示意圖
   由於順序隊列初始狀態沒有存儲任何元素,因此 top 指針和 rear 指針重合,且由於順序隊列底層實現靠的是數組,因此 top 和 rear 實際上是兩個變量,它的值分別是隊頭元素和隊尾元素所在數組位置的下標。
在圖 1 的基礎上,當有數據元素進隊列時,對應的實現操作是將其存儲在指針 rear 指向的數組位置,然后 rear+1;當需要隊頭元素出隊時,僅需做 top+1 操作。
 
例如,在圖 1 基礎上將  {1,2,3,4} 用順序隊列存儲的實現操作如圖 2 所示:
                 數據進順序隊列的過程實現示意圖
                      圖 2 數據進順序隊列的過程實現示意圖
在圖 2 基礎上,順序隊列中數據出隊列的實現過程如圖 3 所示:
 
               數據出順序隊列的過程示意圖
                          圖 3 數據出順序隊列的過程示意圖
 因此,使用順序表實現順序隊列最簡單方法的 C 語言實現代碼為:
#include <stdio.h>
int enQueue(int *a,int rear,int data){
    a[rear]=data;
    rear++;
    return rear;
}
void deQueue(int *a,int front,int rear){
    //如果 front==rear,表示隊列為空
    while (front!=rear) {
        printf("出隊元素:%d\n",a[front]);
        front++;
    }
}
int main() {
    int a[100];
    int front,rear;
    //設置隊頭指針和隊尾指針,當隊列中沒有元素時,隊頭和隊尾指向同一塊地址
    front=rear=0;
    //入隊
    rear=enQueue(a, rear, 1);
    rear=enQueue(a, rear, 2);
    rear=enQueue(a, rear, 3);
    rear=enQueue(a, rear, 4);
    //出隊
    deQueue(a, front, rear);
    return 0;
}

程序輸出結果:

出隊元素:1
出隊元素:2
出隊元素:3
出隊元素:4

此方法存在的問題

先來分析以下圖 2b) 和圖 3b)。圖 2b) 是所有數據進隊成功的示意圖,而圖 3b) 是所有數據全部出隊后的示意圖。通過對比兩張圖,你會發現,指針 top 和 rear 重合位置指向了  a[4] 而不再是 a[0]。也就是說,整個順序隊列在數據不斷地進隊出隊過程中,在順序表中的位置不斷后移。

順序隊列整體后移造成的影響是:
  • 順序隊列之前的數組存儲空間將無法再被使用,造成了空間浪費;
  • 如果順序表申請的空間不足夠大,則直接造成程序中數組 a 溢出,產生溢出錯誤;
為了避免以上兩點,我建議初學者使用下面的方法實現順序隊列。

順序隊列另一種實現方法

既然明白了上面這種方法的弊端,那么我們可以試着在它的基礎上對其改良。
  為了解決以上兩個問題,可以使用巧妙的方法將順序表打造成一個環狀表,如圖 4 所
                環狀順序隊列
                    圖 4 環狀順序隊列
圖 4 只是一個想象圖,在真正的實現時,沒必要真創建這樣一種結構,我們還是使用之前的順序表,也還是使用之前的程序,只需要對其進行一點小小的改變:
 
#include <stdio.h>
#define max 5//表示順序表申請的空間大小
int enQueue(int *a,int front,int rear,int data){
    //添加判斷語句,如果rear超過max,則直接將其從a[0]重新開始存儲,如果rear+1和front重合,則表示數組已滿
    if ((rear+1)%max==front) {
        printf("空間已滿");
        return rear;
    }
    a[rear%max]=data;
    rear++;
    return rear;
}
int  deQueue(int *a,int front,int rear){
    //如果front==rear,表示隊列為空
    if(front==rear%max) {
        printf("隊列為空");
        return front;
    }
    printf("%d ",a[front]);
    //front不再直接 +1,而是+1后同max進行比較,如果=max,則直接跳轉到 a[0]
    front=(front+1)%max;
    return front;
}
int main() {
    int a[max];
    int front,rear;
    //設置隊頭指針和隊尾指針,當隊列中沒有元素時,隊頭和隊尾指向同一塊地址
    front=rear=0;
    //入隊
    rear=enQueue(a,front,rear, 1);
    rear=enQueue(a,front,rear, 2);
    rear=enQueue(a,front,rear, 3);
    rear=enQueue(a,front,rear, 4);
    //出隊
    front=deQueue(a, front, rear);
    //再入隊
    rear=enQueue(a,front,rear, 5);
    //再出隊
    front=deQueue(a, front, rear);
    //再入隊
    rear=enQueue(a,front,rear, 6);
    //再出隊
    front=deQueue(a, front, rear);
    front=deQueue(a, front, rear);
    front=deQueue(a, front, rear);
    front=deQueue(a, front, rear);
    return 0;
}

程序運行結果:

 1 2 3 4 5 6
 使用此方法需要注意的是,順序隊列在判斷數組是否已滿時,出現下面情況:
  • 當隊列為空時,隊列的頭指針等於隊列的尾指針;
  • 當數組滿員時,隊列的頭指針等於隊列的尾指針;
順序隊列的存儲狀態不同,但是判斷條件相同。為了對其進行區分,最簡單的解決辦法是:犧牲掉數組中的一個存儲空間,判斷數組滿員的條件是:尾指針的下一個位置和頭指針相遇,就說明數組滿了,即程序中第 5 行所示。
 
鏈式隊列,簡稱"鏈隊列",即使用鏈表實現的隊列存儲結構。
鏈式隊列的實現思想同 順序隊列類似,只需創建兩個指針(命名為 top 和 rear)分別指向鏈表中隊列的隊頭元素和隊尾元素,如  1 所示:
                鏈式隊列的初始狀態
                      圖 1 鏈式隊列的初始狀態
圖 1 所示為鏈式隊列的初始狀態,此時隊列中沒有存儲任何數據元素,因此 top 和 rear 指針都同時指向頭節點。
 在創建鏈式隊列時,強烈建議初學者創建一個帶有頭節點的鏈表,這樣實現鏈式隊列會更簡單。
  由此,我們可以編寫出創建鏈式隊列的 C 語言實現代碼為:
//鏈表中的節點結構
typedef struct QNode{
    int data;
    struct QNode * next;
}QNode;
//創建鏈式隊列的函數
QNode * initQueue(){
    //創建一個頭節點
    QNode * queue=(QNode*)malloc(sizeof(QNode));
    //對頭節點進行初始化
    queue->next=NULL;
    return queue;
}

鏈式隊列數據入隊

鏈隊隊列中,當有新的數據元素入隊,只需進行以下 3 步操作:

  1. 將該數據元素用節點包裹,例如新節點名稱為 elem;
  2. 與 rear 指針指向的節點建立邏輯關系,即執行 rear->next=elem;
  3. 最后移動 rear 指針指向該新節點,即 rear=elem;

由此,新節點就入隊成功了。

例如,在圖 1 的基礎上,我們依次將 {1,2,3} 依次入隊,各個數據元素入隊的過程如圖 2 所示:

 
               {1,2,3} 入鏈式隊列
                        圖 2 {1,2,3} 入鏈式隊列
數據元素入鏈式隊列的 C 語言實現代碼為:    
QNode* enQueue(QNode * rear,int data){
    //1、用節點包裹入隊元素
    QNode * enElem=(QNode*)malloc(sizeof(QNode));
    enElem->data=data;
    enElem->next=NULL;
    //2、新節點與rear節點建立邏輯關系
    rear->next=enElem;
    //3、rear指向新節點
    rear=enElem;
    //返回新的rear,為后續新元素入隊做准備
    return rear;
}

鏈式隊列數據出隊

當鏈式隊列中,有數據元素需要出隊時,按照 "先進先出" 的原則,只需將存儲該數據的節點以及它之前入隊的元素節點按照原則依次出隊即可。這里,我們先學習如何將隊頭元素出隊。

鏈式隊列中隊頭元素出隊,需要做以下 3 步操作:

  1. 通過 top 指針直接找到隊頭節點,創建一個新指針 p 指向此即將出隊的節點;
  2. 將 p 節點(即要出隊的隊頭節點)從鏈表中摘除;
  3. 釋放節點 p,回收其所占的內存空間;


例如,在圖 2b) 的基礎上,我們將元素 1 和 2 出隊,則操作過程如圖 3 所示

 
                 鏈式隊列中數據元素出隊
                      圖 3 鏈式隊列中數據元素出隊

鏈式隊列中隊頭元素出隊的 C 語言實現代碼為:
 
void DeQueue(QNode * top,QNode * rear){
    if (top->next==NULL) {
        printf("隊列為空");
        return ;
    }
    // 1、
    QNode * p=top->next;
    printf("%d",p->data);
    top->next=p->next;
    if (rear==p) {
        rear=top;
    }
    free(p);
}

注意,將隊頭元素做出隊操作時,需提前判斷隊列中是否還有元素,如果沒有,要提示用戶無法做出隊操作,保證程序的健壯性。

總結

通過學習鏈式隊列最基本的數據入隊和出隊操作,我們可以就實際問題,對以上代碼做適當的修改。

前面在學習順序隊列時,由於順序表的局限性,我們在順序隊列中實現數據入隊和出隊的基礎上,又對實現代碼做了改進,令其能夠充分利用數組中的空間。鏈式隊列就不需要考慮空間利用的問題,因為鏈式隊列本身就是實時申請空間。因此,這可以算作是鏈式隊列相比順序隊列的一個優勢。

這里給出鏈式隊列入隊和出隊的完整 C 語言代碼為:

 
#include <stdio.h>
#include <stdlib.h>
typedef struct QNode{
    int data;
    struct QNode * next;
}QNode;
QNode * initQueue(){
    QNode * queue=(QNode*)malloc(sizeof(QNode));
    queue->next=NULL;
    return queue;
}
QNode* enQueue(QNode * rear,int data){
    QNode * enElem=(QNode*)malloc(sizeof(QNode));
    enElem->data=data;
    enElem->next=NULL;
    //使用尾插法向鏈隊列中添加數據元素
    rear->next=enElem;
    rear=enElem;
    return rear;
}
QNode* DeQueue(QNode * top,QNode * rear){
    if (top->next==NULL) {
        printf("\n隊列為空");
        return rear;
    }
    QNode * p=top->next;
    printf("%d ",p->data);
    top->next=p->next;
    if (rear==p) {
        rear=top;
    }
    free(p);
    return rear;
}
int main() {
    QNode * queue,*top,*rear;
    queue=top=rear=initQueue();//創建頭結點
    //向鏈隊列中添加結點,使用尾插法添加的同時,隊尾指針需要指向鏈表的最后一個元素
    rear=enQueue(rear, 1);
    rear=enQueue(rear, 2);
    rear=enQueue(rear, 3);
    rear=enQueue(rear, 4);
    //入隊完成,所有數據元素開始出隊列
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    return 0;
}

程序運行結果為:

 

1 2 3 4
隊列為空

 


免責聲明!

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



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