一.順序隊列的改進
隊列元素的出列是在隊頭,即下標為0的位置,那也就意味着,隊列中的所有元素都得向前移動,以保證隊列的隊頭(也就是下標為0的位置)不為空,此時的時間復雜度為0(n)。
可有時想想,為什么出隊列時一定要全部移動呢,如果不去限制隊列的元素必須存儲在數組的前n個單元這一條件,出隊的性能就會大大增加。也就是說,隊頭不需要一定在下標為0的位置,比如也可以是a[1]等。
而為了避免當只有一個元素時,隊頭和隊尾重合使處理變得麻煩,引入兩個指針,front指針指向隊頭元素,rear指針指向隊尾元素的下一個位置,這樣當front等於rear時,此隊列不是還剩一個元素,而是空隊列。
對於隊列最好的方法是使用鏈表實現,因為對於數組來說,隊列可能會出現下面這種情況:
假設是長度為5的數組,初始狀態,空隊列如所示,front與 rear指針均指向下標為0的位置。然后入隊a1、a2、a3、a4, front指針依然指向下標為0位置,而rear指針指向下標為4的位置。
出隊a1、a2,則front指針指向下標為2的位置,rear不變,如下圖所示,再入隊a5,此時front指針不變,rear指針移動到數組之外。嗯?數組之外,那將是哪里?
問題還不止於此。假設這個隊列的總個數不超過5個,但目前如果接着入隊的話,因數組末尾元素已經占用,再向后加,就會產生數組越界的錯誤,可實際上,我們的隊列在下標為0和1的地方還是空閑的。我們把這種現象叫做“假溢出”。
不可以繼續添加元素,否則會造成數組越界而遭致程序出錯。然而此時又不應該擴充數組,因為還有大量實際空間未被占用。
此時我們應該如何解決這個問題呢?我們將其實現為循環隊列。
解決假溢出的辦法就是后面滿了,就再從頭開始,也就是頭尾相接的循環。我們把隊列的這種頭尾相接的順序存儲結構稱為循環隊列。
也就是利用循環來解決空間浪費問題。
二.循環隊列的引入
2.1總結來說,什么是循環隊列?
由於在用數組實現隊列的時候,隊列有元素出列,front就向后移動,所以隊列前面的空間就空了出來。當rear移動到LENGTH時,再入隊會發生假溢出,也就是說實際上我們開辟的數組還有剩余空間,卻因為rear越界表現為溢出,
為了更合理的利用空間,人們想了一個辦法:將隊列的首尾相連接。這樣當rear移動到LENGTH時,會再從0開始循環。
那當什么時候隊列滿呢?當rear等於front的時候。可是隊列為空的時候也是同樣的條件,那不就沒法判斷了嗎?
辦法一是設置一個標志變量flag,當front == rear,且flag = 0時為隊列空,當front == rear,且flag= 1時為隊列滿
辦法二犧牲一個存儲空間,front前面不存數據,當rear在front前面的時候就是滿了(尾在頭前就是滿了)
我們主要討論第二種方法=
2.2理解循環隊列
當隊列空時,條件就是from = rear,當隊列滿時,我們修改其條件,保留一個元素空間。也就是說,隊列滿時,數組中還有一個空閑單元。 如下圖所示,我們就認為此隊列已經滿了,
由於rear可能比front大,也可能比front小,所以盡管它們只相差一個位置時就是滿的情況,但也可能是相差整整一圈。所以若隊列的最大尺寸為QueueSize,那么隊列滿的條件是(rear+1) %QueueSize == front (取模“%的目的就是為了整合rear與front大小為一個問題)。
比如上面這個例子, QueueSize = 5,當 front=0,而 rear=4, (4+1) %5 = 0,所以此時隊列滿。再比如,front = 2而rear =1。(1 + 1) %5 = 2,所以此時 隊列也是滿的。而對於下圖, front = 2而rear= 0, (0+1) %5 = 1,1!=2,所以此時隊列並沒有滿。
另外,當rear > front時,此時隊列的長度為rear—front。但當rear < front時,隊列長度分為兩段,一段是QueueSize-front,另一段是0 + rear,加在一起,隊列長度為rear-front + QueueSize,
因此通用的計算隊列長度公式為:
(rear—front + QueueSize) % QueueSize
總結
隊空條件:front == rear
隊滿條件:(rear+1) %QueueSize == front
隊列長度:(rear—front + QueueSize) % QueueSize
三.循環隊列的代碼
3.1.循環隊列各個參數的含義
(1)隊列初始化時,front和rear值都為零;
(2)當隊列不為空時,front指向隊列的第一個元素,rear指向隊列最后一個元素的下一個位置;
(3)當隊列為空時,front與rear的值相等,但不一定為零;
3.2循環隊列入隊的偽算法
(1)把值存在rear所在的位置;
(2)rear=(rear+1)%maxsize ,其中maxsize代表數組的長度;
bool Enqueue(PQUEUE Q, int val)
{
if(FullQueue(Q))
return false;
else
{
Q->pBase[Q->rear]=val;
Q->rear=(Q->rear+1)%Q->maxsize;
return true;
}
}
3.3.循環隊列出隊的偽算法
(1)先保存出隊的值;
(2)front=(front+1)%maxsize ,其中maxsize代表數組的長度;
bool Dequeue(PQUEUE Q, int *val)
{
if(EmptyQueue(Q))
{
return false;
}
else
{
*val=Q->pBase[Q->front];
Q->front=(Q->front+1)%Q->maxsize;
return true;
}
}
3.4.如何判斷循環隊列是否為空
if(front==rear)
隊列空;
else
隊列不空;
bool EmptyQueue(PQUEUE Q)
{
if(Q->front==Q->rear) //判斷是否為空
return true;
else
return false;
}
3.5.如何判斷循環隊列是否為滿
解決這個問題有兩個辦法:一是增加一個參數,用來記錄數組中當前元素的個數;第二個辦法是,少用一個存儲空間,也就是數組的最后一個存數空間不用,當(rear+1)%maxsiz=front時,隊列滿;
bool FullQueue(PQUEUE Q)
{
if(Q->front==(Q->rear+1)%Q->maxsize) //判斷循環鏈表是否滿,留一個預留空間不用
return true;
else
return false;
}
完整代碼
queue.h文件代碼:
#ifndef __QUEUE_H_
#define __QUEUE_H_
typedef struct queue
{
int *pBase;
int front; //指向隊列第一個元素
int rear; //指向隊列最后一個元素的下一個元素
int maxsize; //循環隊列的最大存儲空間
}QUEUE,*PQUEUE;
void CreateQueue(PQUEUE Q,int maxsize);
void TraverseQueue(PQUEUE Q);
bool FullQueue(PQUEUE Q);
bool EmptyQueue(PQUEUE Q);
bool Enqueue(PQUEUE Q, int val);
bool Dequeue(PQUEUE Q, int *val);
#endif
queue.c文件代碼:
#include<stdio.h>
#include<stdlib.h>
#include"malloc.h"
#include"queue.h"
/***********************************************
Function: Create a empty stack;
************************************************/
void CreateQueue(PQUEUE Q,int maxsize)
{
Q->pBase=(int *)malloc(sizeof(int)*maxsize);
if(NULL==Q->pBase)
{
printf("Memory allocation failure");
exit(-1); //退出程序
}
Q->front=0; //初始化參數
Q->rear=0;
Q->maxsize=maxsize;
}
/***********************************************
Function: Print the stack element;
************************************************/
void TraverseQueue(PQUEUE Q)
{
int i=Q->front;
printf("隊中的元素是:\n");
while(i%Q->maxsize!=Q->rear)
{
printf("%d ",Q->pBase[i]);
i++;
}
printf("\n");
}
bool FullQueue(PQUEUE Q)
{
if(Q->front==(Q->rear+1)%Q->maxsize) //判斷循環鏈表是否滿,留一個預留空間不用
return true;
else
return false;
}
bool EmptyQueue(PQUEUE Q)
{
if(Q->front==Q->rear) //判斷是否為空
return true;
else
return false;
}
bool Enqueue(PQUEUE Q, int val)
{
if(FullQueue(Q))
return false;
else
{
Q->pBase[Q->rear]=val;
Q->rear=(Q->rear+1)%Q->maxsize;
return true;
}
}
bool Dequeue(PQUEUE Q, int *val)
{
if(EmptyQueue(Q))
{
return false;
}
else
{
*val=Q->pBase[Q->front];
Q->front=(Q->front+1)%Q->maxsize;
return true;
}
}
參考資料:
http://blog.csdn.net/lpp0900320123/article/details/20694409
http://blog.csdn.net/zx249388847/article/details/51672129
https://www.cnblogs.com/chenliyang/p/6554141.html