Java循環(huán)隊列原理與用法詳解
本文實例講述了Java循環(huán)隊列原理與用法。分享給大家供大家參考,具體如下:
在正式進(jìn)行循環(huán)隊列學(xué)習(xí)之前,我們先來看看在順序隊列中刪除隊首元素出現(xiàn)的問題
(1)設(shè)一個容量為capacity=8,size=5(a,b,c,d,e)的數(shù)組,左側(cè)為隊首、右側(cè)為隊尾。
(2)出隊一個元素后,需整體往前移動一位
#出隊
#2整體前移一位
關(guān)于該種操作方式我們很容易得出時間復(fù)雜度為O(n)。
這時我們就想可不可以在出隊元素后,整體元素不往前移,而是在數(shù)組中記下隊首front是誰,同時隊尾tail指向在下一次元素入隊時的位置,這樣當(dāng)再有出隊時只需要維護(hù)一下front的指向即可,而不需移動元素。就這樣我們就有了循環(huán)隊列的情況。
2.循環(huán)隊列原理
(1)初始,數(shù)組整體為空時,隊首front、隊尾tail指向同一個位置(數(shù)組索引為0的地方)也即front==tail 時隊列為空
(2)當(dāng)往數(shù)組中添加元素后,
(3)出隊一個元素,front指向新的位置
(4)入隊元素,tail疊加
(5)當(dāng)tail不能再增加時,數(shù)組前面還有空余,此時循環(huán)隊列就該出場了。
此時數(shù)組應(yīng)該變?yōu)檫@樣:
在往數(shù)組中添加一個元素:
這樣數(shù)組就已經(jīng)滿了(tail+1==front 隊列滿),開始出發(fā)擴(kuò)容操作?!綾apacity中,浪費(fèi)一個空間】。
為了tail能返回到數(shù)組的前面位置,將隊列滿的表達(dá)式變?yōu)?【(tail+1)%c==front】這樣數(shù)組就可以循環(huán)移動了。
3.循環(huán)隊列代碼實現(xiàn)
新建一個類LoopQueue并實現(xiàn)接口Queue。
#1:接口Queue代碼如下:
package Queue; public interface Queue<E> { //獲取隊列中元素個數(shù) int getSize(); //隊列中元素是否為空 boolean isEmpty(); //入隊列 void enqueue(E e); //出隊列 E dequeue(); //獲取隊首元素 E getFront(); }
#2:LoopQueue相關(guān)代碼:
package Queue; //循環(huán)隊列 public class LoopQueue<E> implements Queue<E> { private E[] data; private int front, tail; private int size;//隊列中元素個數(shù) //構(gòu)造函數(shù),傳入隊列的容量capacity構(gòu)造函數(shù) public LoopQueue(int capacity) { data = (E[]) new Object[capacity + 1];//浪費(fèi)與一個空間 front = 0; tail = 0; size = 0; } //無參構(gòu)造函數(shù),默認(rèn)隊列的容量capacity=10 public LoopQueue() { this(10); } //真正容量 public int getCapacity() { return data.length - 1; } //隊列是否為空 @Override public boolean isEmpty() { return front == tail; } //隊列中元素個數(shù) @Override public int getSize() { return size; } //入隊列操作 @Override public void enqueue(E e) { if ((tail + 1) % data.length == front) {//隊列已滿,需要擴(kuò)容 resize(getCapacity() * 2); } data[tail] = e; tail = (tail + 1) % data.length; size++; } //出隊操作 @Override public E dequeue() { if (isEmpty()) { throw new IllegalArgumentException("隊列為空"); } E ret = data[front]; data[front] = null;//手動釋放 front = (front + 1) % data.length; size--; if (size == getCapacity() / 4 && getCapacity() / 2 != 0) { resize(getCapacity() / 2); } return ret; } //獲取隊首元素 @Override public E getFront() { if (isEmpty()) { throw new IllegalArgumentException("隊列為空"); } return data[front]; } //改變?nèi)萘? private void resize(int newCapacity) { E[] newData = (E[]) new Object[newCapacity + 1]; for (int i = 0; i < size; i++) { newData[i] = data[(front + i) % data.length];//循環(huán)數(shù)組防止越界 } data = newData; front = 0; tail = size; } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append(String.format("Queue:size=%d, capacity=%d\n", size, getCapacity())); res.append("front ["); for (int i = front; i != tail; i = (i + 1) % data.length) { res.append(data[i]); if ((i + 1) % data.length != tail) { res.append(","); } } res.append("] tail"); return res.toString(); } }
在關(guān)于LoopQueue類中需要注意的:
(1)第11行中的+1是capacity需要浪費(fèi)一個空間,故在實例化是多加1
data = (E[]) new Object[capacity + 1];//浪費(fèi)與一個空間
(2)地24行真正的容量是data.length-1,這是由于有一個空間是浪費(fèi)的。
data.length - 1;
(3)關(guān)于入隊中第46行tail值的說明
為了保證入隊是循環(huán)操作,tail值的變化規(guī)律為
tail = (tail + 1) % data.length;
(4)關(guān)于82行的數(shù)據(jù)遷移操作,取余操作是為了防止循環(huán)數(shù)組時越界。
newData[i] = data[(front + i) % data.length];//循環(huán)數(shù)組防止越界
#3直接在LoopQueue中添加一個main函數(shù)進(jìn)行測試,相關(guān)代碼如下:
//測試用例 public static void main(String[] args) { LoopQueue<Integer> queue = new LoopQueue<Integer>(); for (int i = 0; i < 10; i++) { queue.enqueue(i); System.out.println(queue); if(i%3==2){//每添加3個元素出隊列一個 queue.dequeue(); System.out.println(queue); } } }
結(jié)果為:
4.循環(huán)隊列時間復(fù)雜度
到此我們就實現(xiàn)了一個循環(huán)隊列操作,解決了在順序隊列中出隊時的時間復(fù)雜度為O(n)的情況,在循環(huán)隊列中出隊的時間復(fù)雜度為O(1)。
源碼地址 https://github.com/FelixBin/dataStructure/blob/master/src/Queue/LoopQueue.java
更多關(guān)于java算法相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設(shè)計有所幫助。
相關(guān)文章
使用React和springboot做前后端分離項目的步驟方式
這篇文章主要介紹了使用React和springboot做前后端分離項目的步驟方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08Maven根據(jù)不同環(huán)境打包不同配置文件的方法
這篇文章主要介紹了Maven根據(jù)不同環(huán)境打包不同配置文件的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08Spring?Boot?+?Spring?Batch?實現(xiàn)批處理任務(wù)的詳細(xì)教程
這篇文章主要介紹了Spring?Boot+Spring?Batch實現(xiàn)批處理任務(wù),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08關(guān)于Java集合框架Collection接口詳解
這篇文章主要介紹了關(guān)于Java集合框架Collection接口詳解,Collection接口是Java集合框架中的基礎(chǔ)接口,定義了一些基本的集合操作,包括添加元素、刪除元素、遍歷集合等,需要的朋友可以參考下2023-05-05SpringBoot在 POM 中引入本地 JAR 包的方法
在開發(fā) Spring Boot 應(yīng)用程序時,您可能需要使用本地 JAR 包來添加自定義庫或功能,本文將介紹在 Spring Boot 項目的 POM 文件中如何引入本地 JAR 包,感興趣的朋友跟隨小編一起看看吧2023-08-08Java實現(xiàn)監(jiān)控多個線程狀態(tài)的簡單實例
下面小編就為大家?guī)硪黄狫ava實現(xiàn)監(jiān)控多個線程狀態(tài)的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03