隊列是一種“先進先出的數據結構”,可分爲靜態隊列和鏈式隊列。靜態隊列一般使用數組實現,數組需要預先定義內存大小,爲了避免內存浪費,一般使用循環隊列。接下來講述循環隊列的原理以及實現代碼。
循環隊列數據結構定義:
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);
}