什么是假溢出?
當我們使用隊列這種基本的數據結構時,很容易發現,隨著入隊和出隊操作的不斷進行,隊列的數據區域不斷地偏向隊尾方向移動。當我們的隊尾指針指向了隊列之外的區域時,我們就不能再進行入隊操作了,而此時由于隊頭已經偏離原來位置,也就意味著實際上隊列并沒有滿。這種實際上還有空間,但是卻發生了溢出的現象就稱為假溢出現象。
假設有以下隊列,初始元素為1、2、3、4、5,隊頭元素是1,隊尾元素是5.
當我們彈出隊頭元素兩次得到隊列:
這個時候top指針向右邊偏移,如果再進行入隊操作我們會發現rear指針已經不能向后移動了,而此時隊列并沒有滿(top前面還有空間)。
這就是假溢出。
如何解決假溢出問題?
為了避免假溢出問題,我們需要對隊列進行優化--循環隊列。
對于前面的問題,我們發現導致假溢出的主要問題就是尾指針rear不能指向可以存放空間的地址,換句話來說就是不能指向前面已經出隊了元素的地址。針對這一問題,我們整個隊列看成一個可以循環的環狀結構,指向隊頭隊尾的指針往后走還能回到原來的位置。
?這樣一來,前面已經出隊元素的空間我們依舊可以存放元素,也就解決了假溢出的問題。(這里rear指向隊尾元素的下一個元素)。
模擬循環隊列
首先假設我們隊列的最大存儲數據個數為k。
用一個數組來模擬循環隊列,并且初始化容量為k+1,隊頭隊尾指針都指向下標為0的元素地址
為什么容量要多一個呢?
為了更好的區分隊列為空和隊列已滿。
?
如何判斷循環隊列是否為空?
if(top==rear)為真則隊列為空
如何判斷循環隊列是否為滿?
由于我們多開辟了一個元素的空間,且這個空間不存放元素,也就意味著,如果已經存了k個元素,此時的rear指向這個空的區域,rear指向空間的下一個空間的元素被top指針指向。
if((rear+1)%(k+1)==top)//真則隊列為滿
如何求得循環隊列的元素個數?
num=(rear+k-top)%(k+1)//num為循環隊列元素個數
由于rear指針可能在top指針的前面,所以我們不能直接使用rear-top.
那么我們可以這么想,之所以rear會出現在top前面,是因為rear已經走了一圈了(假設只多走了一圈),那么rear移動的總距離就是當前元素位置加隊列長度,top移動的距離就是當前下標。
力扣面試題:
622. 設計循環隊列https://leetcode.cn/problems/design-circular-queue/
代碼:
typedef struct {int *val;int front;int back;int k;int size;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue *obj=(MyCircularQueue *)malloc(sizeof(MyCircularQueue));obj->val=(int*)malloc(sizeof(int)*(k+1));obj->front=0;obj->back=0;obj->k=k;obj->size=0;return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return ((obj->back)==(obj->front));
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (((obj->back+1)%(obj->k+1))==obj->front);
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))return false;obj->val[obj->back]=value;obj->back=(obj->back+1)%(obj->k+1);return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return false;obj->front=(obj->front+1)%(obj->k+1);return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->val[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;int k=obj->k;return obj->val[(obj->back+k)%(k+1)];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->val);obj->val=NULL;free(obj);}