1. 隊(duì)列的定義
隊(duì)列(queue)是一種只允許在一端進(jìn)行插入操作,而在另一端進(jìn)行刪除操作的線性表。其嚴(yán)格遵循先進(jìn)先出(First In First Out)的規(guī)則,簡(jiǎn)稱FIFO。
- 隊(duì)頭(Front):允許刪除的一端,又稱隊(duì)首。
- 隊(duì)尾(Rear):允許插入的一端。
2. 隊(duì)列的分類
隊(duì)列與棧類似,實(shí)現(xiàn)方式有兩種。一種是以數(shù)組的方式實(shí)現(xiàn),另一種以單鏈表來實(shí)現(xiàn)。這兩種實(shí)現(xiàn)方式各有優(yōu)劣,并且都有細(xì)節(jié)需要處理。
- 基于單鏈表實(shí)現(xiàn):我們可以將鏈表的頭節(jié)點(diǎn)與尾節(jié)點(diǎn)分別作為隊(duì)列的隊(duì)首與隊(duì)尾,這樣我們就能用兩個(gè)指針來對(duì)其進(jìn)行操作。如下圖:
- 基于數(shù)組實(shí)現(xiàn):我們同樣可以通過兩個(gè)下標(biāo)分別指向數(shù)組的起始與結(jié)束,但這時(shí)我們就可能發(fā)現(xiàn)兩個(gè)問題:
-
問題一:在不斷出隊(duì)與進(jìn)隊(duì)得到過程中,起始下標(biāo)與末尾下標(biāo)都在向后移動(dòng),當(dāng)兩個(gè)下標(biāo)同時(shí)指向數(shù)組末尾時(shí)就無法再移動(dòng)了,并且**浪費(fèi)前面大量空間,**如圖1
-
問題二:為了解決上述問題,我們將數(shù)組首尾相接變?yōu)?strong>循環(huán)數(shù)組。但這時(shí)又會(huì)出現(xiàn)一個(gè)問題,那便是當(dāng)隊(duì)首與隊(duì)尾下標(biāo)指向同一個(gè)節(jié)點(diǎn)時(shí),這個(gè)隊(duì)列到底是空還是滿呢?這時(shí)我們有三個(gè)解決方法
-
第一種:犧牲一個(gè)單元來區(qū)分隊(duì)空和隊(duì)滿,這時(shí)若隊(duì)列不為空,讓隊(duì)尾下標(biāo)指向隊(duì)尾的下一個(gè)位置。約定以隊(duì)頭指針在隊(duì)尾指針的下一位置作為隊(duì)滿的標(biāo)志,即
Q->rear+1==Q->front
。如圖二。 -
第二種:增設(shè)表示元素個(gè)數(shù)的數(shù)據(jù)成員
size
。這樣,隊(duì)空的條件為Q->size==0
;隊(duì)滿的條件為Q->size==MaxSize
。 -
第三種:增加表示隊(duì)滿的數(shù)據(jù)成員
flag
。將flag初始化為0,當(dāng)隊(duì)滿時(shí)將其置為1。
3. 隊(duì)列的功能
- 隊(duì)列的初始化。
- 判斷隊(duì)列是否為空。
- 判斷隊(duì)列是否滿了。
- 返回隊(duì)頭與隊(duì)尾的元素。
- 返回隊(duì)列的大小。
- 入隊(duì)與出隊(duì)。
- 打印隊(duì)列的元素。
- 銷毀隊(duì)列。
4. 隊(duì)列的聲明
4.1. 鏈?zhǔn)疥?duì)
鏈?zhǔn)疥?duì)的聲明十分簡(jiǎn)單,參照上面圖我們就可以直接實(shí)現(xiàn)了。
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* rear;
size_t size;
}Queue;
4.2. 循環(huán)隊(duì)
根據(jù)上述分析,我們采用一個(gè)數(shù)組來實(shí)現(xiàn)隊(duì)列,其聲明如下
typedef int QDataType;
#define MAXSIZE 50 //定義元素的最大個(gè)數(shù)
/*循環(huán)隊(duì)列的順序存儲(chǔ)結(jié)構(gòu)*/
typedef struct {
QDataType *data;
int front; //頭指針
int rear; //尾指針
}Queue;
5. 隊(duì)列的初始化
對(duì)隊(duì)列聲明的數(shù)據(jù)進(jìn)行初始化,防止隨機(jī)值。
5.1. 鏈?zhǔn)疥?duì)
void QueueInit(Queue* q)
{
q->front = NULL;
q->rear = NULL;
q->size = 0;
}
5.2. 循環(huán)隊(duì)
void QueueInit(Queue* q)
{
q->data = (QDataType*)malloc(sizeof(QDataType )* MAXSIZE);
if (q->data == NULL)
{
perror("malloc fail:");
return;
}
q->front = 0;
q->rear = 0;
}
5.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:鏈?zhǔn)疥?duì)空間是一個(gè)固定大小,空間復(fù)雜度為O(1)。而需要開辟整個(gè)隊(duì)列的大小,空間復(fù)雜度為O(N)。
6. 判斷隊(duì)列是否為空
判斷隊(duì)列是否為空十分簡(jiǎn)單,這里就不在贅述。
6.1. 鏈?zhǔn)疥?duì)
bool QueueEmpty(Queue* q)
{
assert(q);
return (q->front == NULL) && (q->rear == NULL);
}
6.2. 循環(huán)隊(duì)
bool QueueEmpty(Queue*q)
{
assert(q);
return q->front == q->rear;
}
6.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
7. 判斷隊(duì)列是否為滿
7.1. 鏈?zhǔn)疥?duì)
鏈?zhǔn)疥?duì)不需要判斷是否為滿的情況。
7.2. 循環(huán)隊(duì)
為了完成循環(huán)操作,需要對(duì)其進(jìn)行取模操作,防止越界。
bool QueueFull(Queue*q)
{
assert(q);
return q->front == (q->rear+1)%MAXSIZE;
}
7.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
8. 返回隊(duì)頭與隊(duì)尾元素
因?yàn)槎x了頭指針與尾指針,所以訪問數(shù)據(jù)也十分方便。
8.1. 鏈?zhǔn)疥?duì)
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->front->data;
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->rear->data;
}
8.2. 循環(huán)隊(duì)
rear
下標(biāo)可能指向下標(biāo)0,這時(shí)貿(mào)然減一就可能出現(xiàn)越界的情況。為了解決這個(gè)問題我們可以加上MAXSIZE
再對(duì)其取模。
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->data[q->front];
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->data[(q->rear-1+MAXSIZE)%MAXSIZE];
}
8.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
9. 隊(duì)列的大小
9.1. 鏈?zhǔn)疥?duì)
size_t QueueSize(Queue* q)
{
return q->size;
}
9.2. 循環(huán)隊(duì)
求循環(huán)隊(duì)列的大小,我們很容易想到用Q->rear-Q->front
得出隊(duì)列元素個(gè)數(shù)。但是我們要考慮到一種特殊情況:當(dāng)隊(duì)列先刪除元素再添加元素時(shí),末尾下標(biāo)**rear**
可能循環(huán)重置,如下圖。
那到底該如何解決這個(gè)問題呢?其實(shí)我們只需要在原來基礎(chǔ)上加上一個(gè)MAXSIZE
就行了,為了使圖一情況也適用我們?nèi)孕枘I弦粋€(gè)MAXSIZE
。
size_t QueueSize(Queue*q)
{
assert(q);
return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}
9.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
10. 入隊(duì)
10.1. 鏈?zhǔn)疥?duì)
鏈?zhǔn)疥?duì)列入隊(duì)時(shí)需要判斷隊(duì)列是否為空的特殊情況,如果是則還需要將尾指針也指向這個(gè)節(jié)點(diǎn)。
void QueuePush(Queue* q, QDataType x)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
newnode->data = x;
newnode->next = NULL;
if (newnode == NULL)
{
perror("malloc fail");
return;
}
if (q->front == NULL)
{
q->front = q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
q->size++;
}
10.2. 循環(huán)隊(duì)
為了使循環(huán)隊(duì)列在插入數(shù)據(jù)時(shí)實(shí)現(xiàn)循環(huán)操作,我們可以每次進(jìn)行取模操作。并且每次入隊(duì)之前要檢查循環(huán)隊(duì)是否已滿。
void QueuePush(Queue* q, QDataType x)
{
assert(q);
if(QueueFull)
{
printf("隊(duì)列已滿\n");
return ;
}
q->data[q->rear] = x;
q->rear = (q->rear + 1) % MAXSIZE; //rear指針向后移一位置,若到最后則轉(zhuǎn)到數(shù)組頭部
}
10.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
11. 出隊(duì)
11.1. 鏈?zhǔn)疥?duì)
同樣考慮特殊情況,防止隊(duì)列為空。并且當(dāng)隊(duì)列只有一個(gè)節(jié)點(diǎn)時(shí)需要將頭指針與尾指針都置為空。
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
//1.只有一個(gè)結(jié)點(diǎn)
if (q->front == q->rear)
{
free(q->front);
q->front = q->rear = NULL;
}
//2.有多個(gè)結(jié)點(diǎn)
else
{
QNode* del = q->front;
q->front = q->front->next;
free(del);
del = NULL;
}
q->size--;
}
11.2. 循環(huán)隊(duì)
同樣為了實(shí)現(xiàn)循環(huán),我們可以進(jìn)行取模操作。文章來源:http://www.zghlxwxcb.cn/news/detail-844261.html
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
q->front = (q->front + 1) % MAXSIZE; //front指針向后移一位置,若到最后則轉(zhuǎn)到數(shù)組頭部
}
11.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
12. 打印隊(duì)列
12.1. 鏈?zhǔn)疥?duì)
void QueuePrint(Queue* q)
{
assert(q);
QNode* cur = q->front;
QNode* tail = q->rear;
printf("隊(duì)頭->");
while (cur != tail->next)
{
printf("%d->",cur->data);
cur = cur->next;
}
printf("隊(duì)尾\n");
}
12.2. 循環(huán)隊(duì)
void QueuePrint(Queue* q)
{
assert(q);
int cur = q->front;
printf("隊(duì)頭->");
while (cur != q->rear)
{
printf("%d->", q->data[cur]);
cur = (cur + 1) % MAXSIZE;
}
printf("隊(duì)尾\n");
}
12.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
13. 銷毀隊(duì)列
13.1. 鏈?zhǔn)疥?duì)
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
del = NULL;
}
q->front = q->rear = NULL;
}
13.2. 循環(huán)隊(duì)
void QueueDestroy(Deque* q)//銷毀隊(duì)列
{
assert(q);
free(q->data);
q->data = NULL;
q->front = q->rear = 0;
}
13.3. 復(fù)雜度分析
- 時(shí)間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)時(shí)間都是一個(gè)常數(shù),所以時(shí)間復(fù)雜度為O(1)。
- 空間復(fù)雜度:無論是鏈?zhǔn)疥?duì)還是循環(huán)隊(duì)列花費(fèi)空間都是一個(gè)固定大小,所以空間復(fù)雜度為O(1)。
14. 鏈?zhǔn)疥?duì)與循環(huán)隊(duì)列的對(duì)比與應(yīng)用
14.1. 對(duì)比
對(duì)比項(xiàng) | 鏈?zhǔn)疥?duì) | 循環(huán)隊(duì)列 |
---|---|---|
時(shí)間效率 | 因?yàn)榇嬖陬^指針與尾指針,所以鏈?zhǔn)疥?duì)的出隊(duì)與入隊(duì)的時(shí)間都相對(duì)較小。 | 循環(huán)隊(duì)列是基于數(shù)組實(shí)現(xiàn)的,支持下標(biāo)的隨機(jī)訪問,所以時(shí)間消耗也并不大 |
空間效率 | 鏈?zhǔn)疥?duì)每次入隊(duì)都需固定創(chuàng)造一個(gè)新的節(jié)點(diǎn),空間利用率較高,較穩(wěn)定。 | 循環(huán)隊(duì)列的空間是固定的,可能會(huì)造成空間的浪費(fèi)。 |
14.2. 應(yīng)用
隊(duì)列的應(yīng)用與棧一樣,十分廣泛文章來源地址http://www.zghlxwxcb.cn/news/detail-844261.html
- 當(dāng)我們?nèi)ナ程脪叽a訂餐時(shí),你的訂單就會(huì)加入一個(gè)隊(duì)列中。
- 在操作系統(tǒng)中,隊(duì)列可以用來管理任務(wù)進(jìn)度與進(jìn)程切換。
15. 完整代碼
15.1. 鏈?zhǔn)疥?duì)
15.1.1. Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* rear;
size_t size;
}Queue;
void QueueInit(Queue* q);//初始化隊(duì)列
bool QueueEmpty(Queue* q);//判斷是否為空
QDataType QueueFront(Queue* q);//獲取隊(duì)頭元素
QDataType QueueBack(Queue* q);//或許隊(duì)尾元素
size_t QueueSize(Queue* q);//或許隊(duì)列長(zhǎng)度
void QueuePush(Queue* q, QDataType x);//入隊(duì)
void QueuePop(Queue* q);//出隊(duì)
void QueuePrint(Queue* q);//打印隊(duì)列元素
void QueueDestroy(Queue* q);//銷毀隊(duì)列
15.1.2. Queue.c
#include"Queue.h"
void QueueInit(Queue* q)
{
q->front = NULL;
q->rear = NULL;
q->size = 0;
}
bool QueueEmpty(Queue* q)
{
assert(q);
return (q->front == NULL) && (q->rear == NULL);
}
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->front->data;
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->rear->data;
}
size_t QueueSize(Queue* q)
{
return q->size;
}
void QueuePush(Queue* q, QDataType x)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
newnode->data = x;
newnode->next = NULL;
if (newnode == NULL)
{
perror("malloc fail");
return;
}
if (q->front == NULL)
{
q->front = q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
q->size++;
}
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
//1.只有一個(gè)結(jié)點(diǎn)
if (q->front == q->rear)
{
free(q->front);
q->front = q->rear = NULL;
}
//2.有多個(gè)結(jié)點(diǎn)
else
{
QNode* del = q->front;
q->front = q->front->next;
free(del);
del = NULL;
}
q->size--;
}
void QueuePrint(Queue* q)
{
assert(q);
QNode* cur = q->front;
QNode* tail = q->rear;
printf("隊(duì)頭->");
while (cur != tail->next)
{
printf("%d->",cur->data);
cur = cur->next;
}
printf("隊(duì)尾\n");
}
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
del = NULL;
}
q->front = q->rear = NULL;
}
15.2. 循環(huán)隊(duì)列
15.2.1. Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
#define MAXSIZE 50 //定義元素的最大個(gè)數(shù)
/*循環(huán)隊(duì)列的順序存儲(chǔ)結(jié)構(gòu)*/
typedef struct {
QDataType *data;
int front; //頭指針
int rear; //尾指針
}Queue;
void QueueInit(Queue* q);//初始化隊(duì)列
bool QueueEmpty(Queue* q);//判斷是否為空
bool QueueFull(Queue*q);//判斷隊(duì)列是否滿
QDataType QueueFront(Queue* q);//獲取隊(duì)頭元素
QDataType QueueBack(Queue* q);//或許隊(duì)尾元素
size_t QueueSize(Queue* q);//或許隊(duì)列長(zhǎng)度
void QueuePush(Queue* q, QDataType x);//入隊(duì)
void QueuePop(Queue* q);//出隊(duì)
void QueuePrint(Queue* q);//打印隊(duì)列元素
15.2.2. Queue.c
void QueueInit(Queue* q)
{
q->data = (QDataType*)malloc(sizeof(QDataType )* MAXSIZE);
if (q->data == NULL)
{
perror("malloc fail:");
return;
}
q->front = 0;
q->rear = 0;
}
bool QueueEmpty(Queue*q)
{
assert(q);
return q->front == q->rear;
}
bool QueueFull(Queue*q)
{
assert(q);
return q->front == (q->rear+1)%MAXSIZE;
}
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->data[q->front];
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->data[(q->rear-1+MAXSIZE)%MAXSIZE];
}
size_t QueueSize(Queue*q)
{
assert(q);
return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}
void QueuePush(Queue* q, QDataType x)
{
assert(q);
if(QueueFull)//判斷隊(duì)列是否滿了
{
printf("隊(duì)列已滿\n");
return ;
}
q->data[q->rear] = x;
q->rear = (q->rear + 1) % MAXSIZE; //rear指針向后移一位置,若到最后則轉(zhuǎn)到數(shù)組頭部
}
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
q->front = (q->front + 1) % MAXSIZE; //front指針向后移一位置,若到最后則轉(zhuǎn)到數(shù)組頭部
}
void QueuePrint(Queue* q)
{
assert(q);
int cur = q->front;
printf("隊(duì)頭->");
while (cur != q->rear)
{
printf("%d->", q->data[cur]);
cur = (cur + 1) % MAXSIZE;
}
printf("隊(duì)尾\n");
}
void QueueDestroy(Deque* q)//銷毀隊(duì)列
{
assert(q);
free(q->data);
q->data = NULL;
q->front = q->rear = 0;
}
到了這里,關(guān)于探索數(shù)據(jù)結(jié)構(gòu):鏈?zhǔn)疥?duì)與循環(huán)隊(duì)列的模擬、實(shí)現(xiàn)與應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!