之前寫過一篇部落格介紹隊列:http://blog.csdn.net/thefutureisour/article/details/7835273
後來發現這個這個隊列是有問題的:
bool enQueue(Queue *q,ElemType e){//如果隊列已滿,重新分配記憶體if(q->rear == q->Qsize-1){q->data = (ElemType*)realloc(q->data,2*q->Qsize*sizeof(ElemType));if(q->data == NULL)return false;elseq->Qsize *= 2;}//先賦值,然後隊尾迴圈加1q->data[q->rear] = e;q->rear = (q->rear+1)%q->Qsize;return true;}
這個判斷隊列滿的辦法明顯是錯誤的,因為有可能在多次出隊、入隊操作以後,data[1]代表rear,data[0]代表front,此時隊列已經滿了應該重新分配記憶體,而這個程式並沒有做出正確的判斷。然後我對作了修改,改成了
if(Qlength(q) == q->Qsize-1)
這個代碼看似正確,但是如果進行下面的測試,就會發現問題了:
int a = 100;Queue myQueue;initQueue(&myQueue,4);//先把隊列充滿for(int i = 0 ; i < 3 ;++i)enQueue(&myQueue,i);printQueue(&myQueue);deQueue(&myQueue,&a);enQueue(&myQueue,3);printQueue(&myQueue);deQueue(&myQueue,&a);enQueue(&myQueue,4);printQueue(&myQueue);enQueue(&myQueue,5);printQueue(&myQueue);destroyQueue(&myQueue);
最後一次入隊5以後,列印為空白,通過單步調試,我發現問題因為此時Qlength的傳回值為0.我才恍然大悟,問題的嚴重性:
當data[0]為rear而data[1]為front時,雖然這時候隊列實際已經滿了,我也重新分配了記憶體,但是rear和front下標卻沒有修改,進而導致重新分配的後的數組是類似於下面的結構:
data 0 1 2 3 4 5 6 7
rear front 資料 資料 { 新分配的空間 }
首先:enqueue以後rear會++,所以此時會出現Qlength為0,其次這已經完全不是迴圈隊列的結構了,rear的前一個資料是沒有初始化的記憶體。
怎麼解決這個問題呢?
我也沒有特別高明的辦法,只能重新分配的時候建立一個數組,數組中的元素按照隊列從頭至尾的順序儲存,然後重新分配記憶體之後,把新記憶體的前Qsize-1個元素用建立的數組初始化,然後把rear置為Qsize,新來的元素放到rear的位置,然後rear再自增。這樣就搞定了:
bool enQueue(Queue *q,ElemType e){//如果隊列已滿,重新分配記憶體if(q->Qsize-1 == Qlength(q)){//先把原來的按順序記錄在tmp中ElemType* tmp = (ElemType*)malloc(sizeof(ElemType) * q->Qsize);for(int i = 0; i< q->Qsize-1;++i){tmp[i] = q->data[(q->front+i+q->Qsize)%q->Qsize];}//重新分配記憶體q->data = (ElemType*)realloc(q->data,2*q->Qsize*sizeof(ElemType));if(q->data == NULL)return false;//用tmp初始化q->datafor(int i = 0; i< q->Qsize-1;++i){q->data[i] = tmp[i];}//重新設定front、rear標記q->front= 0;q->rear = q->Qsize;//新來的元素放入隊尾q->data[q->Qsize] = e;//隊尾指標自增q->rear++;//容量翻倍q->Qsize *= 2;free(tmp);}else{//先賦值,然後隊尾迴圈加1q->data[q->rear] = e;q->rear = (q->rear+1)%q->Qsize;}return true;}
由於時間匆忙,可能會有小的紕漏,但是大方向上應該是沒有問題的。