1. 程式人生 > >佇列的實現(1):迴圈佇列的實現

佇列的實現(1):迴圈佇列的實現

佇列是一種“先進先出的資料結構”,可分為靜態佇列和鏈式佇列。靜態佇列一般使用陣列實現,陣列需要預先定義記憶體大小,為了避免記憶體浪費,一般使用迴圈佇列。接下來講述迴圈佇列的原理以及實現程式碼。

迴圈佇列資料結構定義:
int front;//指向佇列頭,指向第一個資料節點
int rear;//指向佇列尾(並不是指向最後一個數據節點,而是最後一個數據節點後面的位置)
char data[];//節點資料,根據實際需要可以是不同的資料型別,但是因為是陣列,需要在宣告時指定大小

迴圈佇列操作方法:

void circQue_Init(circQue_t* queue);`//初始化data陣列的資料,front和rear設為0
void circQue_Deinit(circQue_t* queue);//銷燬佇列 bool isQueEmpty(circQue_t* queue);//檢查佇列是否為空,在獲取佇列最前面的資料前需要檢查佇列是否為空,判斷依據是front是否和rear相等(不一定都是0) bool isQueFull(circQue_t* queue);//檢查佇列是否已滿,在往佇列插入新的節點前需要檢查佇列是否已經滿,判斷依據是(rear+1)%dataLen == front,因為rear永遠指向最後一個節點後面的位置,也就是說陣列大小雖然是dataLen,但是實際最多隻能儲存dataLen-1個節點 bool
circQue_add(circQue_t* queue, char newData);//往佇列插入節點(即佇列最後一個節點) char circQue_remove(circQue_t* queue);//從佇列移除節點(即佇列第一個節點) void printQueueData(circQue_t* queue);//列印佇列資料

通常會比較疑惑的是,為什麼rear不指向最後一個數據節點,而是指向最後一個節點後面的位置?
考慮這個情況:為什麼我們要用(rear+1)%dataLen == front作為佇列滿的條件?因為如果使用rear==front作為判斷條件,那麼就和佇列空的判斷條件重複了,無法知道到底是滿還是空。因此需要預留一個空間,當rear的後一個位置是front,說明佇列已經滿了。

佇列滿的情況:

還有一個容易犯的錯誤,迴圈佇列的遍歷。簡單的陣列或者單鏈表的遍歷,都是從首位置開始,然後每次++,但是迴圈佇列存在這樣的情況:rear比front小,這個時候如果從front開始,每次自增1直到等於rear,條件將永遠不成立。
舉個例子,一個最多可儲存5個元素的迴圈佇列

插入三個節點,此時:
front為0,rear為3
將兩個節點移除出佇列,此時:
front為2,rear為3
插入三個節點,此時:
front為2,rear為1

可以看到,這就是迴圈佇列的特點,rear的值已經比front小了

因此,採用下面的迴圈遍歷佇列:

for(int i = queue->front; i != queue->rear; i=(i+1)%MAX_NODE_NUM)

i = queue->front; while(i% MAX_NODE_NUM!= queue ->rear){…;i++}

以下是實現迴圈佇列的原始碼:

#include <string.h>
#include <malloc.h>
#include <iostream>

#define MAX_NODE_NUM 10

using namespace std;

typedef struct circQueue{
    int front;
    int rear;
    char data[MAX_NODE_NUM];
}circQue_t; 

void circQue_Init(circQue_t* queue){        
    queue->front = 0;
    queue->rear = 0;
    memset(queue->data, 0, MAX_NODE_NUM);
}

void circQue_Deinit(circQue_t* queue){
    if(queue){
        cout << "will free queue,front is:" << queue->front <<",rear is:" << queue->rear << endl;
        free(queue);
    }
}

bool isQueEmpty(circQue_t* queue){
    return (queue->front == queue->rear);//when front equal rear means circular queque is empty
}

bool isQueFull(circQue_t* queue){
    return (((queue->rear + 1) % MAX_NODE_NUM == queue->front));//get the value of rear plus 1, then mod capacity of circular queue, queue is full if the result of mod is equal front  
}

bool circQue_add(circQue_t* queue, char newData){
    if(isQueFull(queue)){
        cout << "queue is full, add node failed!" << endl;  
        return false;   
    }
    queue->data[queue->rear] = newData;
    queue->rear = (queue->rear+1)%MAX_NODE_NUM;
    cout << "front is:" << queue->front << ",rear is:" << queue->rear << endl;
    return true;
}

char circQue_remove(circQue_t* queue){
    if(isQueEmpty(queue)){
        cout << "queue is empty, remove node failed!" << endl;  
        return 'x';         
    }
    char nodeValue = queue->data[queue->front];
    queue->front = (queue->front+1)%MAX_NODE_NUM;
    return  nodeValue;
}

void printQueueData(circQue_t* queue){
    for(int i = queue->front; i != queue->rear; i=(i+1)%MAX_NODE_NUM){
        cout << queue->data[i] << " ";
    }
    cout << endl;
}

int main(){
    circQue_t* que = (circQue_t*)malloc(sizeof(circQue_t));
    if(!que){
        cout << "malloc for circular queque failed!" << endl;
        return -1;  
    }   

    circQue_Init(que);

    circQue_add(que, 'a');
    circQue_add(que, 'b');
    circQue_add(que, 'c');
    printQueueData(que);
    circQue_remove( que);
    printQueueData(que);
}