隊列的順序及鏈式實現

隊列定義

  隊列的工作原理和現實中的隊列完全一致,比如排隊上車,排前面的先上車,排後面的後上車。隊列只支持兩種操作,入隊和出隊,是一種先進先出的數據結構(First In First Out,FIFO),順序和鏈式兩種結構的隊列如下:
隊列存儲結構

隊列基本操作

  1. 初始化隊列 initQueue():初始化隊列空間及隊首隊尾指針 head、tail
  2. 判斷隊列是否爲空 isEmpty():當隊首、隊尾相等時,隊爲空,即 head == tail 是返回真,否返回假
  3. 入隊 enQueue():將元素從隊尾 tail 處加入隊列,並且更新隊尾指針,即 tail++
  4. 出隊 delQueue():從隊首元素開始出隊,並且更新隊首指針,即 head++
  5. 讀隊首元素 getHeadValue():讀取隊首元素的值

順序隊列實現

#include <stdio.h>
#include <string.h>
#define true 1
#define false 0
#define MAX_SIZE 100

typedef struct {
    char data[MAX_SIZE];
    int head;
    int tail;
}Queue;

void initQueue(Queue *queue){
    memset(queue -> data, 0, MAX_SIZE);
    queue -> head = 0;
    queue -> tail = 0;
    printf("######## 隊列初始化完成! ########\n");
}

int isEmpty(Queue *queue){
    if(queue -> head == queue -> tail) {
    printf("完成出隊! Queue -> head = Queue -> tail = %d\n", queue -> head);
        return true;
    }
    return false;
}

char getHeadValue(Queue *queue){
    return queue -> data[queue -> head];
}

void enQueue(Queue *queue, char value){
    queue -> data[queue -> tail] = value;
    queue -> tail++;
    printf("元素 %c 入隊!\n",queue -> data[queue -> tail - 1]);
}

void delQueue(Queue *queue){
    printf("元素 %c 將出隊!\n",getHeadValue(queue));
    queue -> data[queue -> head] = 0;
    queue -> head++;
}

int main()
{
    Queue Q;
    initQueue(&Q);
    char str[MAX_SIZE] = {0};
    printf("請輸入數據,按Enter結束!\n");
    scanf("%s", str);
    int i;
    printf("######### 開始入隊 #########\n");
    for(i = 0; i < strlen(str); i++) {
        enQueue(&Q, str[i]);
    }
    printf("######### 開始出隊 #########\n");
    while(1) {
        if(isEmpty(&Q)) {
            break;
        }
        delQueue(&Q);
    }
    return 0;
}

該程序模擬隊列基本操作,執行過程如下:
順序隊列

鏈式隊列實現

鏈式隊列通過鏈表來存儲數據,因此需要建立一個鏈表結構(關於鏈表看這裏),在此基礎上再建立隊列結構,定義如下:

typedef struct ListNode{
    char data;
    struct ListNode *next;
}Lnode;

typedef struct ListQueue{
    Lnode *head;
    Lnode *tail;
}Lqueue;
初始化鏈式隊列

鏈式隊列頭尾指針指向同一個空節點

void Init_ListQueue(Lqueue *queue) {
    queue -> head = queue -> tail = (Lnode *)malloc(sizeof(Lnode));
    if(queue -> head == NULL) {
        printf("Malloc Failed!\n");
        exit(0);
    }
    queue -> head -> next = NULL;
}
元素入隊

由隊尾添加元素,隊尾指針始終指向新增節點(node)的地址,移動 tail 指針

void Push_ListValue(Lqueue *queue, char value) {
    Lnode *node = (Lnode *)malloc(sizeof(Lnode));
    node -> data = value;
    node -> next = NULL;
    queue -> tail -> next = node;
    queue -> tail = queue -> tail -> next;
    printf("Push: %c\n", queue -> tail -> data);
}
元素出隊

移動隊頭指針出隊,head -> next 指向第一個節點地址,當 head -> next 等於隊尾指針時,已到鏈隊末尾,隊尾指針出隊,出隊結束,釋放 head -> next 空間,鏈隊空!

void Pop_ListValue(Lqueue *queue) {
    if(queue -> head -> next == queue -> tail) {
        printf("Pop: %c\n", queue -> tail -> data);
        free(queue -> head -> next);
        queue -> head -> next = NULL;
        printf("隊列已空!\n");
        return;
    }
    else {
        Lnode *tmp = queue -> head -> next;
        printf("Pop: %c\n", tmp -> data);
        queue -> head -> next = tmp -> next;
        free(tmp);
        tmp = NULL;
    }
}
測試代碼

當隊列爲空時,需要釋放頭節點指針 LQ.head. 此處多一次Pop操作來驗證隊列是否已釋放空

void test_1() {
    Lqueue LQ;
    Init_ListQueue(&LQ);
    Push_ListValue(&LQ, 'A');
    Push_ListValue(&LQ, 'B');
    Push_ListValue(&LQ, 'C');
    Pop_ListValue(&LQ);
    Pop_ListValue(&LQ);
    Pop_ListValue(&LQ);
    Pop_ListValue(&LQ);
    free(LQ.head);
}

執行結果打印如下:

Push: A
Push: B
Push: C
Pop: A
Pop: B
Pop: C
隊列已空!

循環隊列

  循環隊列最多存儲(MAXSIZE - 1)個隊列元素,因此當循環隊列中只剩下一個空存儲單元時,說明隊列已經滿了,即隊列判滿條件是 head = (tail + 1) % MAXSIZE;隊列判空的條件是 head == tail;
在實際開發中,比較常見的隊列是循環雙端隊列,就是隊列兩端都可以進出的隊列,如下圖:
   雙端隊列
對於循環雙端隊列的實現,Leetcode上面有一道題目,小編之前有總結一篇博客,這裏貼出鏈接C語言實現循環雙端隊列
好了,關於隊列的學習總結就到這了,之後有新的感悟再做更新…

參考文章

關於鏈式隊列的一篇博客

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