循環隊列


一.順序隊列的改進

隊列元素的出列是在隊頭,即下標為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

 


免責聲明!

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



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