美團(tuán)一面:循環(huán)隊(duì)列聽說過么,怎么實(shí)現(xiàn)?
順序隊(duì)列
順序隊(duì)列定義
隊(duì)列的底層是數(shù)組,我們常說的隊(duì)列其實(shí)就是順序隊(duì)列,其數(shù)據(jù)結(jié)構(gòu)定義一般是:
- 隊(duì)頭指針指向數(shù)組第一個(gè)元素
- 隊(duì)尾指針指向數(shù)組最后一個(gè)元素的下一個(gè)位置
為了避免當(dāng)只有一個(gè)元素時(shí),隊(duì)頭和隊(duì)尾重合使處理變得麻煩,所以這里引入了隊(duì)頭和隊(duì)尾兩個(gè)指針,假設(shè) front 指針指向隊(duì)頭元素,rear 指針指向隊(duì)尾元素的下一個(gè)位置,這樣:
- 當(dāng) front == rear 時(shí),表示這個(gè)隊(duì)列是空隊(duì)列
- 當(dāng) front == rear + 1 時(shí),表示這個(gè)隊(duì)列中只有一個(gè)元素
示意圖如下:
圖片
眾所周知,隊(duì)列是先進(jìn)先出的,那么進(jìn)隊(duì)操作對(duì)應(yīng)的步驟就是:先送值到隊(duì)尾,再將隊(duì)尾指針 +1
// 送值到隊(duì)尾
queue[rear] = x;
// 隊(duì)尾指針 +1
rear ++;
出隊(duì)操作:先取出隊(duì)頭元素,再將隊(duì)頭指針 +1
// 取出隊(duì)頭元素
x = queue[Q.front]
// 隊(duì)頭指針 +1
front ++;
假溢出問題
順序隊(duì)列存在假溢出問題 ,就是明明在隊(duì)列中仍然有可以存放元素的空間卻無法執(zhí)行入隊(duì)操作了,舉個(gè)例子:
隊(duì)列的大小是 5(數(shù)組容量為 5),一開始是空隊(duì)列,然后依次入隊(duì)了 A、B、C、D:
圖片
然后 A 出隊(duì),B 出隊(duì),相應(yīng)的 front 指針會(huì)往后移動(dòng)兩位:
圖片
再入隊(duì)一個(gè)新元素 E,此時(shí) front 指針不變,rear 指針需要 +1,已經(jīng)超出了數(shù)組的下標(biāo)范圍,就會(huì)導(dǎo)致新元素插入失敗:
圖片
明明隊(duì)列中還有空間,插入元素竟然會(huì)失???這就是一種假性上溢出現(xiàn)象。
如何解決這個(gè)問題呢,有三種:
- 建立一個(gè)足夠大的存儲(chǔ)空間以避免溢出。這樣做空間使用率低,浪費(fèi)存儲(chǔ)空間
- 移動(dòng)元素:每當(dāng)出隊(duì)一個(gè)元素,就將移動(dòng)隊(duì)列中所有的已有元素向隊(duì)頭移動(dòng)一個(gè)位置。這樣做很明顯時(shí)間復(fù)雜度比較高,效率慢
- 循環(huán)隊(duì)列:將隊(duì)頭和隊(duì)尾看作是一個(gè)首尾相接的循環(huán)隊(duì)列
因此,循環(huán)隊(duì)列是解決順序隊(duì)列假溢出問題的最佳選擇!
循環(huán)隊(duì)列
循環(huán)隊(duì)列的數(shù)據(jù)結(jié)構(gòu)定義一般是:
- 隊(duì)列長(zhǎng)度固定,即隊(duì)列(數(shù)組)容量有限
- 隊(duì)列的頭尾相接形成一個(gè)環(huán),當(dāng)隊(duì)尾到達(dá)數(shù)組的最后一個(gè)位置時(shí),下一個(gè)位置是數(shù)組的第一個(gè)位置
具體實(shí)現(xiàn)步驟如下:
- 定義一個(gè)數(shù)組和兩個(gè)指針:front 和 rear,分別表示隊(duì)頭和隊(duì)尾的位置。初始時(shí)(空隊(duì)列),隊(duì)頭和隊(duì)尾都指向數(shù)組的第一個(gè)位置,即 front = rear = 0。
- 入隊(duì)時(shí),首先檢查隊(duì)列是否已滿,如何判斷隊(duì)列滿?犧牲一個(gè)單元來區(qū)分隊(duì)空和隊(duì)滿:即 (rear + 1) % maxsize = front。如果滿了則返回錯(cuò)誤,否則將元素添加到隊(duì)尾,即 queue[rear] = element,然后將 rear 指針向后移動(dòng)一位,即 rear = (rear + 1) % capacity。
- 出隊(duì)時(shí),首先檢查隊(duì)列是否為空,**front == rear 就表示隊(duì)列空**。如果為空則返回錯(cuò)誤,否則將隊(duì)頭元素取出并返回,即 element = queue[front],然后將 front 指針向后移動(dòng)一位,即 front = (front + 1) % capacity。
- 在隊(duì)列的任何時(shí)刻,隊(duì)列中的元素?cái)?shù)量為 (rear - front + capacity) % capacity
示意圖如下:
圖片
以下是一個(gè)基于數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列的 Java 代碼示例:
public class CircularQueue {
// 存儲(chǔ)元素的數(shù)組
private int[] data;
private int front, rear;
// 數(shù)組大小
private int capacity;
public CircularQueue(int k) {
capacity = k;
data = new int[capacity];
front = 0;
rear = 0;
}
// 入隊(duì)
public boolean enqueue(int element) {
if (isFull()) {
return false;
} else {
data[rear] = element;
rear = (rear + 1) % capacity;
return true;
}
}
// 出隊(duì)
public boolean dequeue() {
if (isEmpty()) {
return false;
} else {
front = (front + 1) % capacity;
return true;
}
}
// 獲取隊(duì)頭元素
public int front() {
if (isEmpty()) {
return -1;
} else {
return data[front];
}
}
// 獲取隊(duì)尾元素
public int rear() {
if (isEmpty()) {
return -1;
} else {
return data[(rear - 1 + capacity) % capacity];
}
}
// 判斷隊(duì)列是否為空
public boolean isEmpty() {
return front == rear;
}
// 判斷隊(duì)列是否滿
public boolean isFull() {
return (rear + 1) % capacity == front;
}
}
簡(jiǎn)單總結(jié)就是:
- 初始/隊(duì)空:front = rear
- 出隊(duì):front = (front + 1) % capacity (最大元素個(gè)數(shù))
- 進(jìn)隊(duì):rear = (rear + 1) % capacity
- 隊(duì)列長(zhǎng)度:(rear - front + capacity) % capacity
- 隊(duì)滿(犧牲一個(gè)單元來區(qū)分隊(duì)空和隊(duì)滿 ):(rear + 1) % capacity = front