【數據結構】「隊列」(順序隊列、鏈式隊列、雙端隊列)

- 第 112篇 -
Date: 2025 - 07 - 20
Author: 鄭龍浩(仟墨)

文章目錄

  • 隊列(Queue)
    • 1 基本介紹
      • 1.1 定義
      • 1.2 棧 與 隊列的區別
      • 1.3 重要術語
    • 2 基本操作
    • 3 順序隊列(循環版本)
      • 兩種版本
      • 兩種版本區別
      • 版本1.1 - rear指向隊尾后邊 且 無 size 或 tag
        • SeqQueue.h
        • SeqQueue.c
      • 版本1.2 - rear指向隊尾后邊 且 有 size
        • SeqQueue.h
        • SeqQueue.c
      • 版本1.3 - rear指向隊尾后邊 且 有 tag
        • SeqQueue.h
        • SeqQueue.c
    • 4 鏈式隊列
      • 4.1 兩種版本區別
      • 4.2 鏈式隊列_帶頭結點
      • 4.3 鏈式隊列_無頭結點
    • 5 雙端隊列

隊列(Queue)

1 基本介紹

1.1 定義

隊列(Queue)是一種先進先出(FIFO, First-In-First-Out)的線性數據結構,只允許在隊尾插入(入隊)、在隊頭刪除(出隊)

1.2 棧 與 隊列的區別

  • 棧 是只允許一端進行插入或刪除的線性表 –> 所以后進先出(在棧頂進行插入刪除)
  • 隊列 是只允許在一端進行插入,在另一端刪除的線性表 –> 所以先進先出(在隊尾插入,隊頭刪除)
  • 先進先出:FIFO(First In First Out)

1.3 重要術語

隊頭、隊尾、空隊尾

隊頭:允許刪除的一端

隊尾:允許插入的一端

空隊尾:沒有任何元素的隊列

2 基本操作

  • 初始化:InitQueue(*Q)
  • 清空:ClearQueue(*Q)
  • 銷毀:DestroyQueue(*Q)
  • 入隊:EnQueue(*Q, ElemType x)
  • 出隊:DeQueue(*Q, ElelType* x)
  • 讀取隊頭元素:GetHeadQueue(*Q, ElemType* x)
  • 判斷是否為空:IsEmpty(*Q)
  • 判斷是否已滿:IsFull(Queue *Q)(只有順序隊列有這個)
  • 隊列長度:QueueLength(Queue *Q)
  • 打印:PrintQueue(*Q)

3 順序隊列(循環版本)

兩種版本

有好幾個版本

注意:只有帶size或tag參數的隊列結構不需要浪費空間,即浪費最后一個存儲單元,不帶這倆參數的版本,如果不空出最后一個存儲單元,就無法通過 rear == fron 判斷出來:是滿?是空?

當空出一個存儲單元的時候就可以判斷出是滿還是空了

所以如果不帶size或tag參數就必然要浪費一個存儲單元,但是從整的結構來說,這樣反而節省了空間,因為帶了size或者tag的參數,每個節點都會多個變量,所以實際上來說,并沒節省。

版本1 - 指向指向隊尾元素的下一個位置(即隊尾后邊)

  • 版本1.1 - rear 指向隊尾后邊 且 結構中無size或tag參數
    • 必須空出最后一個存儲單元
    • 初始化時,各參數默認為:
      • front = 0
      • rear = 0
    • 隊空條件front == rear
    • 隊滿條件(rar + 1) % MaxSize == front
  • 版本1.2 - raer 指向隊尾后邊 且 結構中有size參數
    • 不需要空出任何存儲單元
    • 初始化時,各參數默認為:
      • front = 0
      • rear = 0
      • size = 0
    • 可以直接通過size判斷隊列的狀態
    • 隊空條件:size = 0
    • 隊滿條件:size == MaxSize
  • 版本1.3 - rear 指向隊尾后邊 且 結構中有tag參數
    • 不需要空出任何存儲單元
    • 初始化時,各參數默認為:
      • front = 0
      • rear = 0
      • tag= 0(初始化相當于上一次操作出隊)
    • 通過tag標記出最后一次操作是入隊還是出隊(0是出隊,1是入隊)
    • 隊空條件front == rear && tag == 0(如果上一次操作是出隊tag==0,出現front == rear 就是隊空
    • 隊滿條件front == rear && tag == 1(如果上一次操作是入隊tag==1,出現front == raer 就是隊滿

版本2 - rear 指向隊尾元素

  • 版本2.1 - rear 指向隊尾 且 結構中無size或tag參數

    • 必須空出最后一個存儲單元
    • 初始化時,各參數默認為:
      • front = 0
      • rear = -1
    • 隊空條件(rear + 1) % MaxSize == front
    • 隊滿條件(rear + 2) % MaxSize == front
  • 版本2.2 - rear 指向隊尾 且 結構中有size參數

    • 不需要空出存儲單元
    • 初始化時,各參數默認為:
      • front = 0
      • rear = -1
      • size = 0
    • 通過size變量直接判斷隊列的狀態
    • 隊空條件size == 0
    • 隊滿條件size == MaxSize
  • 版本2.3 - rear 指向隊尾 且 結構中有tag參數

    • 不需要空出存儲單元

    • 初始化時,各參數默認為:

      • front = 0
      • rear = -1
      • tag= 0(初始化相當于上一次操作出隊)
    • 通過tag標記出最后一次操作是出隊還是入隊(0是出隊,1是入隊)

    • 隊空條件front == (rear + 1) % MaxSize && tag == 0

    • 隊滿條件front == (rear + 1) % MaxSize && tag == 1

兩種版本區別

版本1:rear 指向隊尾的下一個位置

front 指向隊頭元素,rear 指向下一個可插入的位置。隊列長度計算為 (rear - front + MaxSize) % MaxSize,隊空條件是 front == rear,隊滿條件是 (rear + 1) % MaxSize == front。此版本邏輯清晰,計算簡單,是最常用的實現方式。

版本2:rear 指向隊尾元素

front 指向隊頭元素,rear 直接指向隊尾元素。隊列長度計算為 (rear - front + 1 + MaxSize) % MaxSize,隊空條件是 (rear + 1) % MaxSize == front,隊滿條件是 (rear + 2) % MaxSize == front。此版本需要額外調整計算,容易出錯,一般不建議使用。

版本1.1 - rear指向隊尾后邊 且 無 size 或 tag

  • 先進先出 -> 隊尾進,隊頭出

  • 隊頭指針:front 指向隊元素

  • 隊尾指針:rear 指向隊尾元素的后一個位置

  • 必須知道:即使是順序隊列,隊頭指針也不可能一直指向data[0]

    初始化后,如果一直入隊操作,隊頭指針肯定是一直指向0的,但是如果執行了出隊操作,那么隊頭指針會指向1,再執行出隊操作,會指向2… –> 現在的front指向2,隊頭元素是data[2]

    假設此時隊尾指針指向的是MaxSize-1,然后進行入隊操作,執行完入隊以后,隊尾指針不會指向MaxSize而是0

    也就是要將線性的結構轉換為一個環形的、循環的結構,方法如下:

    Q->rear = (Q->rear + 1) % MaxSize
    

    也就是當rear指向線性結構的末尾的時候要從頭開始

    因為現在的data[0]data[1]都是空的并且訪問data[MaxSize]會越界(因為數組索引是0~MaxSize),所以rear要指向0,如果再入隊操作,在data[0]的位置會存儲一個值,然后rear指向1此時僅有一個data[1]還未存儲數據

    注意了!!!!

    那么這個位置是否可以存儲數據呢?

    答案是否定的, 我們必須犧牲一個元素在隊列的末尾,原因如下:

    因為判斷隊列是否為空以及初始化的時候都是front == rear

    如果真的在執行一次入隊操作,隊尾元素會是data[1],而rear指向了2

    那么此時的frontrear都同時指向2,那么我到底是將這種情況歸為滿隊,還是空隊呢,所以為了避免這種情況的發生,統一約定:

    • 隊列的最后一個位置要空出來(犧牲一個存儲單元)

    • 隊列已滿的條件:rear下一個位置是front,即

      (Q->rear + 1) % MaxSize == Q->front
      
    • 隊列為空的條件:

      Q->front == Q->rear
      
  • 使用模運算將線狀的存儲空間在邏輯上變成了環狀

SeqQueue.h
#pragma once
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
#define MaxSize 10
typedef int ElemType;
typedef struct {ElemType data[MaxSize]; // 靜態數組存放元素int front, rear; // 隊頭指針 隊尾指針
}SeqQueue;// 初始化
void InitQueue(SeqQueue* Q);// 銷毀隊列
void DestroyQueue(SeqQueue* Q);// 清空隊列
void ClearQueue(SeqQueue* Q);// 判斷隊列是否為空
bool IsEmpty(SeqQueue* Q);// 入隊
bool EnQueue(SeqQueue* Q, ElemType x);// 出隊
bool DeQueue(SeqQueue* Q, ElemType* x);// 讀取隊頭元素
bool GetHeadQueue(SeqQueue* Q, ElemType* x);// 判斷隊列是否已滿
bool IsFull(SeqQueue* Q);// 隊列長度
int QueueLength(SeqQueue* Q);// 打印
void PrintQueue(SeqQueue* Q);
SeqQueue.c
#include "sequence_queue.h"
#include "stdio.h"
#include "stdlib.h"// 初始化
void InitQueue(SeqQueue* Q) {// 隊頭 == 隊尾 且都 指向0Q->rear = Q->front = 0;
}// 銷毀隊列
void DestroyQueue(SeqQueue* Q) {Q->front = Q->rear = 0;
}// 清空隊列
void ClearQueue(SeqQueue* Q) {Q->front = Q->rear = 0;
}// 判斷隊列是否為空
bool IsEmpty(SeqQueue* Q) {return Q->front == Q->rear;
}// 入隊
bool EnQueue(SeqQueue* Q, ElemType x) {if (IsFull(Q)) return false; // 隊列已滿,不能入隊Q->data[Q->rear] = x;Q->rear = (Q->rear + 1) % MaxSize; // 隊尾指針要一直保持在 0 ~ MaxSize-1之間return true; // 入隊成功
}// 出隊
bool DeQueue(SeqQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false; // 空隊列不能刪除*x = Q->data[Q->front];Q->front = (Q->front + 1) % MaxSize;return true;
}// 讀取隊頭元素
bool GetHeadQueue(SeqQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false;*x = Q->data[Q->front];return true;
}// 判斷隊列是否已滿
bool IsFull(SeqQueue* Q) {return (Q->rear + 1) % MaxSize == Q->front;
}// 隊列長度
int QueueLength(SeqQueue* Q) {return (Q->rear - Q->front + MaxSize) % MaxSize;
}// 打印
void PrintQueue(SeqQueue* Q) {if (IsEmpty(Q)) {printf("隊列為空\n");return;}printf("隊頭 --> ");int cur = Q->front; // 遍歷下標while (cur != Q->rear) {printf("%d -> ", Q->data[cur]);cur = (cur + 1) % MaxSize;}printf("隊尾\n");
}

版本1.2 - rear指向隊尾后邊 且 有 size

SeqQueue.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define MaxSize 10
typedef int ElemType;typedef struct {ElemType data[MaxSize]; // 靜態數組存放元素int front, rear;        // 隊頭指針和隊尾指針int size;               // 隊列當前長度
} SeqQueue;// 初始化隊列
void InitQueue(SeqQueue* Q);// 銷毀隊列
void DestroyQueue(SeqQueue* Q);// 清空隊列
void ClearQueue(SeqQueue* Q);// 判斷隊列是否為空
bool IsEmpty(SeqQueue* Q);// 判斷隊列是否已滿
bool IsFull(SeqQueue* Q);// 入隊
bool EnQueue(SeqQueue* Q, ElemType x);// 出隊
bool DeQueue(SeqQueue* Q, ElemType* x);// 讀取隊頭元素
bool GetHeadQueue(SeqQueue* Q, ElemType* x);// 獲取隊列長度
int QueueLength(SeqQueue* Q);// 打印隊列
void PrintQueue(SeqQueue* Q);
SeqQueue.c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define MaxSize 10
typedef int ElemType;typedef struct {ElemType data[MaxSize]; // 靜態數組存放元素int front, rear;        // 隊頭指針和隊尾指針int size;               // 隊列當前長度
} SeqQueue;// 初始化隊列
void InitQueue(SeqQueue* Q) {Q->front = Q->rear = 0;Q->size = 0;
}// 銷毀隊列
void DestroyQueue(SeqQueue* Q) {Q->front = Q->rear = 0;Q->size = 0;
}// 清空隊列
void ClearQueue(SeqQueue* Q) {Q->front = Q->rear = 0;Q->size = 0;
}// 判斷隊列是否為空
bool IsEmpty(SeqQueue* Q) {return Q->size == 0;
}// 判斷隊列是否已滿
bool IsFull(SeqQueue* Q) {return Q->size == MaxSize;
}// 入隊
bool EnQueue(SeqQueue* Q, ElemType x) {if (IsFull(Q)) return false;Q->data[Q->rear] = x;Q->rear = (Q->rear + 1) % MaxSize;Q->size++;return true;
}// 出隊
bool DeQueue(SeqQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false;*x = Q->data[Q->front];Q->front = (Q->front + 1) % MaxSize;Q->size--;return true;
}// 讀取隊頭元素
bool GetHeadQueue(SeqQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false;*x = Q->data[Q->front];return true;
}// 獲取隊列長度
int QueueLength(SeqQueue* Q) {return Q->size;
}// 打印隊列
void PrintQueue(SeqQueue* Q) {if (IsEmpty(Q)) {printf("隊列為空\n");return;}printf("隊頭 --> ");int cur = Q->front;for (int i = 0; i < Q->size; i++) {printf("%d -> ", Q->data[cur]);cur = (cur + 1) % MaxSize;}printf("隊尾\n");
}

版本1.3 - rear指向隊尾后邊 且 有 tag

SeqQueue.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define MaxSize 10
typedef int ElemType;typedef struct {ElemType data[MaxSize]; // 靜態數組存放元素int front, rear;        // 隊頭指針和隊尾指針bool tag;               // 最近操作標記: true為入隊, false為出隊
} SeqQueue;// 初始化隊列
void InitQueue(SeqQueue* Q);// 銷毀隊列
void DestroyQueue(SeqQueue* Q);// 清空隊列
void ClearQueue(SeqQueue* Q);// 判斷隊列是否為空
bool IsEmpty(SeqQueue* Q);// 判斷隊列是否已滿
bool IsFull(SeqQueue* Q);// 入隊
bool EnQueue(SeqQueue* Q, ElemType x);// 出隊
bool DeQueue(SeqQueue* Q, ElemType* x);// 讀取隊頭元素
bool GetHeadQueue(SeqQueue* Q, ElemType* x);// 獲取隊列長度
int QueueLength(SeqQueue* Q);// 打印隊列
void PrintQueue(SeqQueue* Q);
SeqQueue.c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define MaxSize 10
typedef int ElemType;typedef struct {ElemType data[MaxSize]; // 靜態數組存放元素int front, rear;        // 隊頭指針和隊尾指針bool tag;               // 最近操作標記: true為入隊, false為出隊
} SeqQueue;// 初始化隊列
void InitQueue(SeqQueue* Q) {Q->front = Q->rear = 0;Q->tag = false;
}// 銷毀隊列
void DestroyQueue(SeqQueue* Q) {Q->front = Q->rear = 0;Q->tag = false;
}// 清空隊列
void ClearQueue(SeqQueue* Q) {Q->front = Q->rear = 0;Q->tag = false;
}// 判斷隊列是否為空
bool IsEmpty(SeqQueue* Q) {return Q->front == Q->rear && Q->tag == false;
}// 判斷隊列是否已滿
bool IsFull(SeqQueue* Q) {return Q->front == Q->rear && Q->tag == true;
}// 入隊
bool EnQueue(SeqQueue* Q, ElemType x) {if (IsFull(Q)) return false;Q->data[Q->rear] = x;Q->rear = (Q->rear + 1) % MaxSize;Q->tag = true;return true;
}// 出隊
bool DeQueue(SeqQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false;*x = Q->data[Q->front];Q->front = (Q->front + 1) % MaxSize;Q->tag = false;return true;
}// 讀取隊頭元素
bool GetHeadQueue(SeqQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false;*x = Q->data[Q->front];return true;
}// 獲取隊列長度
int QueueLength(SeqQueue* Q) {if (IsFull(Q)) return MaxSize;return (Q->rear - Q->front + MaxSize) % MaxSize;
}// 打印隊列
void PrintQueue(SeqQueue* Q) {if (IsEmpty(Q)) {printf("隊列為空\n");return;}printf("隊頭 --> ");int cur = Q->front;int count = QueueLength(Q);for (int i = 0; i < count; i++) {printf("%d -> ", Q->data[cur]);cur = (cur + 1) % MaxSize;}printf("隊尾\n");
}

4 鏈式隊列

4.1 兩種版本區別

函數/操作帶頭結點版本不帶頭結點版本
初始化隊列創建頭結點,front/rear指向頭結點front/rear初始化為NULL
入隊操作直接插入到rear->next需先判斷是否為空隊列再插入
出隊操作刪除front->next結點直接刪除front結點
判斷隊列空檢查front == rear檢查front == NULL
獲取隊頭元素訪問front->next->data訪問front->data
清空隊列釋放所有數據結點,保留頭結點釋放所有結點
銷毀隊列釋放頭結點和隊列結構體直接釋放隊列結構體
隊列長度front->next開始計數front開始計數
打印隊列front->next開始打印front開始打印
內存方面多占用1個頭結點空間不多占用

4.2 鏈式隊列_帶頭結點

#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"typedef int ElemType;
// 結點結構
typedef struct LinkNode {ElemType data;struct LinkNode* next;
}LinkNode;// 鏈式隊列結構
typedef struct LinkQueue {LinkNode *front, *rear;
}LinkQueue;#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef int ElemType;// 結點結構
typedef struct LinkNode {ElemType data;struct LinkNode* next;
} LinkNode;// 鏈式隊列結構
typedef struct LinkQueue {LinkNode *front, *rear;
} LinkQueue;// 函數聲明
// 初始化
void InitQueue(LinkQueue* Q);
// 清空
void ClearQueue(LinkQueue* Q);
// 銷毀
void DestroyQueue(LinkQueue** Q);
// 入隊
bool EnQueue(LinkQueue* Q, ElemType x);
// 出隊
bool DeQueue(LinkQueue* Q, ElemType* x);
// 判斷隊空
bool IsEmpty(LinkQueue* Q);
// 讀取隊頭元素
bool GetHead(LinkQueue* Q, ElemType* x);
// 隊列長度
int QueueLength(LinkQueue* Q);
// 打印
void PrintQueue(LinkQueue* Q);// 函數實現
// 初始化(帶頭結點)
void InitQueue(LinkQueue* Q) {Q->front = Q->rear = (LinkNode*)malloc(sizeof(LinkNode));Q->front->next = NULL; // Q->fornt 是頭結點 再加->next是第一個結點
}// 清空(帶頭結點)
void ClearQueue(LinkQueue* Q) {LinkNode* cur = Q->front->next;// 釋放所有結點while (cur != NULL) {LinkNode* temp = cur;cur=cur->next;free(temp);}Q->rear = Q->front = NULL; // 將頭指針和尾指針指向頭結點并且置空
}// 銷毀(帶頭結點)銷毀要用二級指針
void DestroyQueue(LinkQueue** Q) {ClearQueue(*Q);// 釋放頭結點free((*Q)->front);// 此時頭尾指針指向頭結點,釋放誰都一樣// 釋放隊列結構free(*Q);Q = NULL;
}// 入隊(帶頭結點) 
bool EnQueue(LinkQueue* Q, ElemType x) {LinkNode* NewNode = (LinkNode*)malloc(sizeof(LinkNode));if (!NewNode) return false; // 申請內存失敗返回falseNewNode->data = x; // 新結點存儲值NewNode->next = NULL; // 新結點(最后一個結點)的后驅是NULLQ->rear->next = NewNode; // 新結點插入到rear后邊Q->rear = NewNode; // rear指向剛剛插入的元素return true;
}// 出隊(帶頭結點)
bool DeQueue(LinkQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false; // 空隊列無法出隊LinkNode* first = Q->front->next; // 將隊列第一個存儲單元(要刪除的單元)位置保存下來,以防將頭結點的后繼連接到第二個存儲單元后,第一個存儲單元的位置丟失*x = first->data; // 將隊列第一個數據讀取出來Q->front->next = first->next; // 將頭結點后繼連接到第二個存儲單元--> 這樣第一個存儲單元地址就不會保存在隊列當中了,也就相當于刪除了,或者出隊// 如果只有一個存儲結點的話,那么rear隊尾指針指向的就是第一個存儲單元,當把這最后一個存儲單元刪除的時候rear就不該指向這個“已經刪除的結點”了,而應該指向“頭結點”,相當于初始化了if (Q->rear == first) Q->rear = Q->front;free(first);return true;
}// 判斷隊空(帶頭結點)
bool IsEmpty(LinkQueue* Q) {return Q->front == Q->rear;
}// 讀取隊頭元素
bool GetHead(LinkQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false; // 空隊*x = Q->front->next->data;return true;
}// 隊列長度(帶頭結點)
int QueueLength(LinkQueue* Q) {if (IsEmpty(Q)) return 0;LinkNode* cur = Q->front->next; // 從第一個存儲結點開始int len = 0;while (cur != NULL) {len++;cur = cur->next;}return len;
}// 打印(帶頭結點)
void PrintQueue(LinkQueue* Q) {LinkNode* cur = Q->front->next; // 從第一個存儲結點開始printf("隊頭 -> ");while (cur != NULL) {printf("%d -> ", cur->data);cur = cur->next;}printf("隊尾\n");
}
int main(void) {return 0;
}

4.3 鏈式隊列_無頭結點

#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"typedef int ElemType;
// 結點結構
typedef struct LinkNode {ElemType data;struct LinkNode* next;
}LinkNode;// 鏈式隊列結構
typedef struct LinkQueue {LinkNode *front, *rear;
}LinkQueue;#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef int ElemType;// 結點結構
typedef struct LinkNode {ElemType data;struct LinkNode* next;
} LinkNode;// 鏈式隊列結構
typedef struct LinkQueue {LinkNode *front, *rear;
} LinkQueue;// 函數聲明
// 初始化
void InitQueue(LinkQueue* Q);
// 清空
void ClearQueue(LinkQueue* Q);
// 銷毀
void DestroyQueue(LinkQueue** Q);
// 入隊
bool EnQueue(LinkQueue* Q, ElemType x);
// 出隊
bool DeQueue(LinkQueue* Q, ElemType* x);
// 判斷隊空
bool IsEmpty(LinkQueue* Q);
// 讀取隊頭元素
bool GetHead(LinkQueue* Q, ElemType* x);
// 隊列長度
int QueueLength(LinkQueue* Q);
// 打印
void PrintQueue(LinkQueue* Q);// 函數實現
// 初始化(無頭結點)
void InitQueue(LinkQueue* Q) {// 初始化時,頭尾都指向NULLQ->front = Q->rear = NULL;
}// 清空(無頭結點)
void ClearQueue(LinkQueue* Q) {LinkNode* cur = Q->front;// 釋放所有結點while (cur != NULL) {LinkNode* temp = cur;cur=cur->next;free(temp);}Q->rear = Q->front = NULL; // 將頭指針和尾指針指向NULL
}// 銷毀(無頭結點)應使用二級指針-->因為銷毀后要將Q置空
void DestroyQueue(LinkQueue** Q) {ClearQueue(*Q); // 清空// 釋放隊列結構free(*Q);Q = NULL; // 給Q置空
}// 入隊(無頭結點) 
bool EnQueue(LinkQueue* Q, ElemType x) {// 有頭節點的時候入隊不需要額外判斷是否為空隊,因為頭尾指針此時都是指向的頭結點,操作相同的LinkNode* NewNode = (LinkNode*)malloc(sizeof(LinkNode));if (!NewNode) return false; // 申請內存失敗返回falseNewNode->data = x; // 新結點存儲值NewNode->next = NULL; // 新結點(最后一個結點)的后驅是NULL// 一句:空隊修改隊尾隊頭,不是空隊列只修改尾指針if (IsEmpty(Q)) {// 更新隊頭隊尾指針Q->front = NewNode; // 如果是空隊列,因為沒有頭結點的存在,所以要將這個新的結點放到第一個結點的位置,而Q->front就是指的第一個結點(如果有頭結點的話,指的是頭結點,而Q->front->next指的是第一個結點)Q->rear = NewNode; // 更新尾指針:rear指向剛剛插入的元素} else { // 當不是空隊的時候-Q->rear->next = NewNode; // 新結點插入到rear后邊Q->rear = NewNode; // 更新尾指針:rear指向剛剛插入的元素}return true;
}// 出隊(無頭結點)
bool DeQueue(LinkQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false; // 空隊列無法出隊LinkNode* First = Q->front; // 將隊列第一個存儲單元(要刪除的單元)位置保存下來,以防第二個結點變為第一個結點地址后,第一個結點地址無法釋放*x = First->data; // 將隊列第一個數據讀取出來// 注意:如果是只有一個結點,執行出隊的話,要改變隊尾隊頭指針指向,因為當是空隊的時候隊頭隊尾指針都要指向NULLif (Q->rear == First) {Q->front = NULL;Q->rear = NULL;} else { // 當有多個結點的時候,只需要改變隊頭指向即可Q->front = First->next; // 將第一個結點改為原第二個結點}// 別忘了:釋放原來第一個結點free(First); // 釋放原第一個結點return true;
}// 判斷隊空
bool IsEmpty(LinkQueue* Q) {return Q->front == NULL; // 當是空隊列的時候頭指針指向NULL
}// 讀取隊頭元素(無頭結點)
bool GetHead(LinkQueue* Q, ElemType* x) {if (IsEmpty(Q)) return false; // 空隊*x = Q->front->data;return true;
}// 隊列長度(無頭結點)
int QueueLength(LinkQueue* Q) {if (IsEmpty(Q)) return 0;LinkNode* cur = Q->front; // 從第一個結點開始int len = 0;while (cur != NULL) {len++;cur = cur->next;}return len;
}// 打印(無頭結點)
void PrintQueue(LinkQueue* Q) {LinkNode* cur = Q->front; // 從第一個結點開始printf("隊頭 -> ");while (cur != NULL) {printf("%d -> ", cur->data);cur = cur->next;}printf("隊尾\n");
}
int main(void) {return 0;
}

5 雙端隊列

除了順序隊列和鏈式隊列以外,還有一種隊列叫做雙端隊列

首先,先來將棧、普通隊列雙端隊列比較一下,都為線性表

  • 棧:只允許從一端插入刪除
  • 普通隊列:只允許從一端插入,只允許從另一端刪除
  • 雙端隊列:只允許從兩端插入或刪除 –> 隊頭隊尾都可以插入和刪除
    • 輸入受限的雙端隊列:允許一端插入刪除;另一端只能允許插入 –> 隊頭只能刪除,隊尾既可以插入和刪除
    • 輸出受限的雙端隊列:允許一端插入刪除;另一端只能允許刪除 –> 隊頭可以插入和刪除,隊尾只能插入

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89640.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89640.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89640.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java行為型模式---解釋器模式

解釋器模式基礎概念解釋器模式&#xff08;Interpreter Pattern&#xff09;是一種行為型設計模式&#xff0c;其核心思想是定義一個語言的文法表示&#xff0c;并定義一個解釋器&#xff0c;使用該解釋器來解釋語言中的句子。這種模式將語法解釋的責任分開&#xff0c;使得語法…

[spring6: PointcutAdvisor MethodInterceptor]-簡單介紹

Advice Advice 是 AOP 聯盟中所有增強&#xff08;通知&#xff09;類型的標記接口&#xff0c;表示可以被織入目標對象的橫切邏輯&#xff0c;例如前置通知、后置通知、異常通知、攔截器等。 package org.aopalliance.aop;public interface Advice {}BeforeAdvice 前置通知的標…

地圖定位與導航

定位 1.先申請地址權限(大致位置精準位置) module.json5文件 "requestPermissions": [{"name": "ohos.permission.INTERNET" },{"name": "ohos.permission.LOCATION","reason": "$string:app_name",&qu…

【數據結構】揭秘二叉樹與堆--用C語言實現堆

文章目錄1.樹1.1.樹的概念1.2.樹的結構1.3.樹的相關術語2.二叉樹2.1.二叉樹的概念2.2.特殊的二叉樹2.2.1.滿二叉樹2.2.2.完全二叉樹2.3.二叉樹的特性2.4.二叉樹的存儲結構2.4.1.順序結構2.4.2.鏈式結構3.堆3.1.堆的概念3.2.堆的實現3.2.1.堆結構的定義3.2.2.堆的初始化3.2.3.堆…

區間樹:多維數據的高效查詢

區間樹&#xff1a;多維數據的高效查詢 大家好&#xff0c;今天我們來探討一個在計算機科學中非常有趣且實用的數據結構——區間樹。想象一下&#xff0c;你是一位城市規劃師&#xff0c;需要快速找出某個區域內所有的醫院、學校或商場。或者你是一位游戲開發者&#xff0c;需要…

SQL 魔法:LEFT JOIN 與 MAX 的奇妙組合

一、引言 在數據庫操作的領域中&#xff0c;數據的關聯與聚合處理是核心任務之一。LEFT JOIN作為一種常用的連接方式&#xff0c;能夠將左表中的所有記錄與右表中滿足連接條件的記錄進行關聯&#xff0c;即便右表中沒有匹配的記錄&#xff0c;左表的記錄也會被保留&#xff0c;…

手寫tomcat

package com.qcby.annotation;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;Target(ElementType.TYPE)// 表示該注解只能用于類上 Retention(Retentio…

Android平臺下openssl動態庫編譯

1. 下載Linux平臺下的NDK軟件包 NDK 下載 | Android NDK | Android Developers 下載完成后執行解壓命令 # unzip android-ndk-r27d-linux.zip 2. 下載openssl-1.1.1w源碼包&#xff0c;并解壓 # tar -xzvf openssl-1.1.1w.tar.gz 3. 進入解壓后的openssl-1.1.1w目錄 …

【C++基礎】面試高頻考點解析:extern “C“ 的鏈接陷阱與真題實戰

名稱修飾&#xff08;Name Mangling&#xff09;是C為支持重載付出的代價&#xff0c;而extern "C"則是跨越語言邊界的橋梁——但橋上的陷阱比橋本身更值得警惕 一、extern "C" 的核心概念與高頻考點1.1 鏈接規范與名字改編機制C 為支持函數重載&#xff0…

OpenCV 官翻 4 - 相機標定與三維重建

文章目錄相機標定目標基礎原理代碼配置校準去畸變1、使用 cv.undistort()2、使用**重映射**方法重投影誤差練習姿態估計目標基礎渲染立方體極線幾何目標基礎概念代碼練習從立體圖像生成深度圖目標基礎概念代碼附加資源練習相機標定 https://docs.opencv.org/4.x/dc/dbb/tutori…

Python類中方法種類與修飾符詳解:從基礎到實戰

文章目錄Python類中方法種類與修飾符詳解&#xff1a;從基礎到實戰一、方法類型總覽二、各類方法詳解1. 實例方法 (Instance Method)2. 類方法 (Class Method)3. 靜態方法 (Static Method)4. 抽象方法 (Abstract Method)5. 魔術方法 (Magic Method)三、方法修飾符對比表四、綜合…

VSCode使用Jupyter完整指南配置機器學習環境

接下來開始機器學習部分 第一步配置環境&#xff1a; VSCode使用Jupyter完整指南 1. 安裝必要的擴展 打開VSCode&#xff0c;按 CtrlShiftX 打開擴展市場&#xff0c;搜索并安裝以下擴展&#xff1a; 必裝擴展&#xff1a; Python (Microsoft官方) - Python語言支持Jupyter (Mi…

數據結構與算法之美:拓撲排序

Hello大家好&#xff01;很高興我們又見面啦&#xff01;給生活添點passion&#xff0c;開始今天的編程之路&#xff01; 我的博客&#xff1a;<但凡. 我的專欄&#xff1a;《編程之路》、《數據結構與算法之美》、《C修煉之路》、《Linux修煉&#xff1a;終端之內 洞悉真理…

Ubuntu18.04 系統重裝記錄

Ubuntu18.04 系統重裝記錄 1 安裝google拼音 https://blog.csdn.net/weixin_44647619/article/details/144720947 你好&#xff01; 這是你第一次使用 Markdown編輯器 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章&#xff0c;了解一下Markdo…

Maven常用知識總結

Maven常用知識總結Maven 安裝與配置windows mvn安裝與配置IntelliJ IDEA 配置IntelliJ IDEA 配置系統mavenIntellij IDEA Maven使用IntelliJ IDEA 不能運行項目常見問題pom.xml 常用標簽講解parentgroupId artifactId versiondependencypropertiespluginpackagingdependencyMan…

PHP框架在大規模分布式系統的適用性如何?

曾幾何時&#xff0c;PHP被貼上“只適合小網站”的標簽。但在技術飛速發展的今天&#xff0c;PHP框架&#xff08;如Laravel、Symfony、Hyperf、Swoft等&#xff09; 早已脫胎換骨&#xff0c;勇敢地闖入了大規模分布式系統的疆域。今天&#xff0c;我們就來聊聊它的真實戰斗力…

DC-DC降壓轉換5.5V/3A高效率低靜態同步降壓轉換具有自適應關斷功能

概述&#xff1a;PC1032是一款高效且體積小巧的同步降壓轉換器&#xff0c;適用于低輸入電壓應用。它是緊湊設計的理想解決方案。其2.5V至5.5V的輸入電壓范圍適用于幾乎所有電池供電的應用。在中等至重負載范圍內&#xff0c;它以1.5MHz&#xff08;典型值&#xff09;的PWM模式…

min_25篩學習筆記+牛客多校02E

本來沒有學習這種較難的算法的想法的&#xff0c;因為比賽也做不到這種難度的題&#xff0c; 但是最近打牛客多校02&#xff0c;有一題要求 [1,n][1,n][1,n] 中素數的個數&#xff0c;我以為是像莫反一樣容斥&#xff0c;但是后面感覺不行。賽后知道是用 min_25 篩來求&#xf…

FunASR Paraformer-zh:高效中文端到端語音識別方案全解

項目簡介 FunASR 是阿里巴巴達摩院開源的端到端語音識別工具箱,集成了多種語音識別、語音活動檢測(VAD)、說話人識別等模塊。其中 paraformer-zh 和 paraformer-zh-streaming 是針對中文語音識別任務優化的端到端模型,分別適用于離線和流式場景。Paraformer 采用并行 Tran…

數據結構自學Day9: 二叉樹的遍歷

一、二叉樹的遍歷“遍歷”就是按某種規則 依次訪問樹中的每個節點&#xff0c;確保 每個節點都被訪問一次且只訪問一次遍歷&#xff1a;前序 中序 后序&#xff08;深度優先&#xff09;&#xff0c;層序&#xff08;廣度優先&#xff09;類型遍歷方法特點深度優先遍歷前序、中…