隊列是一種能夠實現「先進先出」的存儲結構。數組
隊列一般能夠分爲兩種類型:函數
1、順序隊列,採用順序存儲,當長度肯定時使用。 順序隊列又有兩種狀況:spa
①使用數組存儲隊列的稱爲靜態順序隊列。指針
②使用動態分配的指針的稱爲動態順序隊列。code
2、鏈式隊列,採用鏈式存儲,長度不肯定時使用(由鏈表實現)。對象
因爲鏈式隊列跟鏈表差很少,因此在這裏只針對循環(環形)隊列來講明並實踐。
循環隊列的兩個參數:
①front,front指向隊列的第一個元素。(front==head)
②rear,rear指向隊列的最後一個有效元素的下一元素。(rear==tail)blog
隊列的兩個基本操做:出隊和入隊。索引
下面是一個循環隊列(基於數組實現)的結構圖:隊列
入隊(尾部入隊)
①將值存入rear所表明的位置。
②rear = (rear+1)%數組的長度。
出隊(頭部出隊)
front = (front+1)%數組的長度。
隊列是否爲空
front和rear的值相等,則該隊列就必定爲空。
隊列是否已滿內存
在循環隊列中,「隊滿」和「隊空」的條件有多是相同的,都是front ==rear,這種狀況下,沒法區別是「隊滿」仍是「隊空」。
針對這個問題,有3種可能的處理方法: 【這裏採用了第3種處理方法】
(1)另設一個標誌以區別是「隊滿」仍是「隊空」。(即入隊/出隊前檢查是否「隊滿」/「隊空」)
(2)設一個計數器,此時甚至還能夠省去一個指針。
(3)少用一個元素空間,即約定隊頭指針在隊尾指針的下一位置時就做爲「隊滿」的標誌,即「隊滿」條件爲:(pQueue->rear+1)%MAX_SIZE == pQueue->front。
從上圖能夠看出,隨着入隊、出隊的進行,會使整個隊列總體向後移動,就會出現上圖中的現象:隊尾指針已經移到了最後,即隊尾出現溢出,沒法再進行入隊操做,然而實際上,此時隊列中還有空閒空間,這種現象稱爲「假溢出」。
解決「假溢出」的三種辦法:
採用了這種頭尾相接的循環隊列後,入隊的隊尾指針加1操做及出隊的隊頭指針加1操做必須作相應的修改,以確保下標範圍爲0~Max_Size-1。對指針進行取模運算,就能使指針到達最大下標位置後回到0,符合「循環」隊列的特色。
所以入隊時隊尾指針加1操做改成: pQueue->tail = (pQueue->tail+1) % MAX_SIZE;
入隊時隊尾指針加1操做改成: pQueue->head = (pQueue->head+1) % MAX_SIZE;
基於數組的動態順序循環隊列的具體實現:
#ifndef MYQUEUEC_H #define MYQUEUEC_H #include <stdio.h> #include <malloc.h> /* 隊列: 只容許在表的一端(隊尾rear)進行插入操做,而在另外一端(隊頭front)進行刪除操做的線性表 * 插入操做簡稱爲入隊 刪除操做簡稱爲出隊 隊列具備先進先出的特色 */ /*=====隊列的入隊、出隊示意圖======== * * 出隊 ----------------- 入隊 * <--- a1,a2,a3,...,an <--- * ----------------- * *================================*/ typedef enum { OK=0, //正確 ERROR=1, //出錯 TRUE=2, //爲真 FALSE=3 //爲假 }status; typedef int ElemType; //宏定義隊列的數據類型 #define MAX_SIZE 20 /*1、使用數組存儲隊列的稱爲靜態順序隊列 *2、使用動態分配的指針的稱爲動態順序隊列*/ // 【這裏的是動態順序隊列】 typedef struct { ElemType *pBase; //數組指針 ElemType front; //隊頭索引 ElemType rear; //隊尾索引 int maxSize; //當前分配的最大容量 }queue; //建立空隊列 queueCapacity-隊列容量 status initQueue(queue *PQueue,int queueCapacity); //銷燬隊列 void destroyQueue(queue *PQueue); //清空隊列 void clearQueue(queue *PQueue); //判斷隊列是否爲空 status isEmpityQueue(queue *PQueue); //判斷隊列是否爲滿 status isFullQueue(queue *PQueue); //得到隊列長度 int getQueueLen(queue *PQueue); //新元素入隊 [先進先出原則:在隊尾的位置插入] element-要插入元素 status enQueue(queue *PQueue,ElemType element); //新元素出隊,同時保存出隊的元素 [先進先出原則:在隊頭的位置刪除] status deQueue(queue *PQueue,ElemType *pElement); //遍歷隊列 void queueTraverse(queue *PQueue); #endif // MYQUEUEC_H
#include "myqueuec.h" /* 隊列: 只容許在表的一端(隊尾rear)進行插入操做,而在另外一端(隊頭front)進行刪除操做的線性表 * 插入操做簡稱爲入隊 刪除操做簡稱爲出隊 隊列具備先進先出的特色 */ /*=====隊列的入隊、出隊示意圖======== * * 出隊 ----------------- 入隊 * <--- a1,a2,a3,...,an <--- * ----------------- * *================================*/ //建立隊列 queueCapacity-隊列容量 status initQueue(queue *PQueue,int queueCapacity) { //給數組指針分配內存 PQueue->pBase = (ElemType *)malloc(sizeof(ElemType)*queueCapacity); if(!PQueue->pBase) { printf("給數組指針分配內存失敗\n"); return ERROR; } PQueue->front = 0; //最開始建立時,隊頭索引爲0 PQueue->rear = 0; //最開始建立時,隊尾索引爲0 PQueue->maxSize = queueCapacity; return OK; } //銷燬隊列 void destroyQueue(queue *PQueue) { free(PQueue); //釋放隊列數組指針指向的內存 PQueue = NULL; //隊列數組指針從新指向NULL,避免成爲野指針 } //清空隊列 void clearQueue(queue *PQueue) { PQueue->front = 0; //隊頭索引清0 PQueue->rear = 0; //隊尾索引清0 } //判斷隊列是否爲空 status isEmpityQueue(queue *PQueue) { if( PQueue->front == PQueue->rear ) //隊頭==隊尾,說明爲空 return TRUE; return FALSE; } /* *在循環隊列中,「隊滿」和「隊空」的條件有多是相同的,都是front==rear, *這種狀況下,沒法區別是「隊滿」仍是「隊空」。 *針對這個問題,有3種可能的處理方法: *(1)另設一個標誌以區別是「隊滿」仍是「隊空」。(即入隊/出隊前檢查是否「隊滿」/「隊空」) *(2)設一個計數器,此時甚至還能夠省去一個指針。 *(3)少用一個元素空間,即約定隊頭指針在隊尾指針的下一位置時就做爲「隊滿」的標誌, *即「隊滿」條件爲:(PQueue->rear+1)%MAX_SIZE == PQueue->front。 * 【這裏採用了第3種處理方法】 */ //判斷隊列是否爲滿 status isFullQueue(queue *PQueue) { if( (PQueue->rear+1)%PQueue->maxSize == PQueue->front ) //隊列滿 return TRUE; return FALSE; } //得到隊列長度 int getQueueLen(queue *PQueue) { //正常狀況下,隊列長度爲隊尾隊頭指針之差,但若是首尾指針跨容量最大值時,要% return (PQueue->rear - PQueue->front + PQueue->maxSize)%PQueue->maxSize; } //新元素入隊 [先進先出原則:在隊尾的位置插入] element-要插入元素 status enQueue(queue *PQueue,ElemType element) { if(isFullQueue(PQueue)==TRUE) { printf("隊列已滿,不能再插入元素了!\n"); return FALSE; } //向隊列中添加新元素 PQueue->pBase[PQueue->rear] = element; PQueue->rear = (PQueue->rear+1) % PQueue->maxSize; //將rear賦予新的合適的值 return TRUE; } //新元素出隊,同時保存出隊的元素 [先進先出原則:在隊頭的位置刪除] status deQueue(queue *PQueue,ElemType *pElement) { //若是隊列爲空,則返回false if(isEmpityQueue(PQueue)==TRUE) { printf("隊列爲空,出隊失敗!\n"); return FALSE; } *pElement = PQueue->pBase[PQueue->front]; //先進先出 PQueue->front = (PQueue->front+1) % PQueue->maxSize; //移到下一位置 return TRUE; } //遍歷隊列 void queueTraverse(queue *PQueue) { int i = PQueue->front; //從頭開始遍歷 printf("遍歷隊列:\n"); while(i != PQueue->rear) //若是沒有到達rear位置,就循環 { printf("%d ", PQueue->pBase[i]); i = (i+1) % PQueue->maxSize; //移到下一位置 } printf("\n"); }
#include <stdio.h> #include "myqueuec.h" int main(void) { int value; //用於保存出隊的元素 //建立隊列對象 queue *PQueue = (queue *)malloc(sizeof(queue)); if(!PQueue->pBase) { printf("給隊列對象分配內存失敗\n"); return -1; } //調用初始化隊列的函數 initQueue(PQueue,MAX_SIZE); //調用出隊函數 enQueue(PQueue, 1); enQueue(PQueue, 2); enQueue(PQueue, 3); enQueue(PQueue, 4); enQueue(PQueue, 5); enQueue(PQueue, 6); enQueue(PQueue, 7); enQueue(PQueue, 8); //調用遍歷隊列的函數 queueTraverse(PQueue); //調用出隊函數 if(deQueue(PQueue, &value)) { printf("出隊一次,元素爲:%d\n", value); } queueTraverse(PQueue); if(deQueue(PQueue, &value)) { printf("出隊一次,元素爲:%d\n", value); } queueTraverse(PQueue); free(PQueue); PQueue = NULL; getchar(); return 0; }