環形隊列


在網上看到一篇比較好的介紹隊列的文章,地址為:http://www.cnblogs.com/kubixuesheng/p/4104802.html

特此感謝原創作者,以下均為摘抄。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1、順序隊列

 限制僅在表頭刪除和表尾插入的順序表,利用一組地址連續的存儲單元依次存放隊列中的數據元素。因為隊頭和隊尾的位置是變化的,所以也要設頭、尾指針。  

 

初始化時的頭尾指針,初始值均應置為 0。 入隊尾指針增 1 ,出隊頭指針增 1 。頭尾指針相等時隊列為空,在非空隊列里,頭指針始終指向隊頭元素,尾指針始終指向隊尾元素的下一位置。

初始為空隊列,那么頭尾指針相等。

入隊,那么尾指針加1,頭指針不變。先進先出,J1先進隊,則 rear+1,尾指針始終指向隊尾元素的下一位!如,J2進隊,rear 繼續+1,J3進隊,尾指針繼續加1,如圖

 

出隊,則尾指針不變,頭指針加1,注意這里都是加1,先進先出原則,J1先刪除,front+1,指向了 J2,J2刪除,front+1指向了 J3,如圖

 

最后,J3刪除,則頭指針再次和尾指針相等,說明隊列空了。如圖

在順序隊列中,當尾指針已經指向了隊列的最后一個位置的下一位置時,若再有元素入隊,就會發生“溢出”。如圖位置,再次入隊,就會溢出。

 

2、循環隊列的誕生

順序隊列的 “假溢出” 問題:隊列的存儲空間未滿,卻發生了溢出。很好理解,比如 rear 現在雖然指向了最后一個位置的下一位置,但是之前隊頭也刪除了一些元素,那么隊頭指針經歷若干次的 +1 之后,遺留下了很多空位置,但是順序隊列還在傻乎乎的以為再有元素入隊,就溢出呢!肯定不合理。故循環隊列誕生!

解決“假溢出”的問題有兩種可行的方法:

(1)、平移元素:把元素平移到隊列的首部。效率低。否決了。

(2)、將新元素插入到第一個位置上,構成循環隊列,入隊和出隊仍按“先進先出”的原則進行。操作效率高、空間利用率高。

      

雖然使用循環隊列,解決了假溢出問題,但是又有新問題發生——判空的問題,因為僅憑 front = rear 不能判定循環隊列是空還是滿。比如如圖:

這是空循環隊列的樣子

這是滿循環隊列的樣子

解決辦法:

(1)、另設一個布爾變量以區別隊列的空和滿;

(2)、少用一個元素的空間,約定入隊前測試尾指針在循環下加 1 后是否等於頭指針,若相等則認為隊滿;(最常用)

(3)、使用一個計數器記錄隊列中元素的總數。

對於第2個方案,必須犧牲一個元素的空間,那么入隊的時候需要測試,循環意義下的加 1 操作可以描述為:

1     if (rear + 1 = MAXQSIZE)
2 
3            rear = 0;
4 
5      else
6 
7           rear ++; 復制代碼

 

利用模運算可簡化為:

 

1 rear = (rear + 1)% MAXQSIZE  

基本操作

復制代碼
 1 #ifndef ___queue_Header_h
 2 #define ___queue_Header_h
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #define MAX_SIZE 5
 6 
 7 typedef struct{
 8     int *base;
 9     int rear;//如果隊列不空,指向隊尾元素的下一個位置
10     int front;//初始的時候指向表頭
11 } CirularQueue;
12 
13 //初始化
14 void initQueue(CirularQueue *queue)
15 {
16     queue->base = (int *)malloc(MAX_SIZE*sizeof(int));
17     
18     if (NULL == queue->base) {
19         exit(0);
20     }
21     
22     queue->front = queue->rear = 0;
23 }
復制代碼

求長度

復制代碼
//求長度
int getLength(CirularQueue queue)
{
    //這樣把所以的情況都考慮到了
    return (queue.rear - queue.front + MAX_SIZE) % MAX_SIZE;
}
復制代碼

第一種情況,長度的求法

第二種情況,長度的求法,利用模運算,兩個情況合二為一!

復制代碼
//入隊,先判滿
void insertQueue(CirularQueue *queue, int e)
{
    if ((queue->rear + 1) % MAX_SIZE == queue->front) {
        puts("循環隊列是滿的!");
    }
    else
    {
        queue->base[queue->rear] = e;
        queue->rear = (queue->rear + 1) % MAX_SIZE;
    }
}
復制代碼

如下時為滿,損失一個空間,不存儲元素。方便判滿

1 //出隊
 2 void deleteQueue(CirularQueue *queue)
 3 {
 4     if (queue->front == queue->rear) {
 5         puts("隊列是空的!");
 6     }
 7     else
 8     {
 9         queue->front = (queue->front + 1) % MAX_SIZE;
10     }
11 }
12 
13 //遍歷
14 void traversal(CirularQueue queue)
15 {
16     int q = queue.front;
17     
18     for (int i = 0; i < getLength(queue); i++) {
19         printf("循環隊列的第%d個元素為%d\n", i + 1, queue.base[q]);
20         q ++; 
21 }
22 }
23
24 #endif

為了尊重原創我將原作者的代碼貼上來了,但對最后一個遍歷持保留意見,我覺得后面直接q++不會有問題嗎?

我個人覺得應該改成:

//遍歷
void traversal(CirularQueue queue)
{
    int q = queue.front;
    
    for (int i = 0; i < getLength(queue); i++) {
        printf("循環隊列的第%d個元素為%d\n", i + 1, queue.base[q]);
        q = (q + 1)%MAX_SIZE; 
    } 
}  

  

 


免責聲明!

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



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