日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构学习笔记(三):队列(queue)

發(fā)布時(shí)間:2025/3/12 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构学习笔记(三):队列(queue) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

1 隊(duì)列的結(jié)構(gòu)形式與操作原則

2 兩種順序隊(duì)列及其代碼實(shí)現(xiàn)(Java)

2.1 簡單隊(duì)列

2.1.1 增刪查操作的實(shí)現(xiàn)

2.1.2 簡單隊(duì)列存在的弊端

2.2 循環(huán)隊(duì)列

3 鏈?zhǔn)疥?duì)列及其代碼實(shí)現(xiàn)(Java)

3.1 鏈?zhǔn)疥?duì)列的設(shè)計(jì)思路

3.2 增刪查操作的實(shí)現(xiàn)


1 隊(duì)列的結(jié)構(gòu)形式與操作原則

隊(duì)列是在兩端分別進(jìn)行增刪操作的線性表。對照棧的數(shù)據(jù)進(jìn)出在同一端的特性,雖然隊(duì)列的兩端都是開放的,但是各自都只有一種功能,一個(gè)為數(shù)據(jù)的進(jìn)口,另一個(gè)為數(shù)據(jù)的出口,像極了一條單行道。我們將隊(duì)列中新增數(shù)據(jù)的一端定義為隊(duì)尾(rear),將刪除數(shù)據(jù)的一端定義為隊(duì)頭(front)。

隊(duì)列的操作原則是先進(jìn)先出(Fist In Fist Out, FIFO),即先進(jìn)來的先出去,后進(jìn)來的后出去。假如我們的“單行道”中依次開進(jìn)6輛車,如下圖:

榮威最先開進(jìn)來,也最先開出去,它不出去否則其他車也出不去;紅旗最后開進(jìn)來,只能等所有前車都開出了才能出去。

隊(duì)列的增刪操作只能分別在隊(duì)頭和隊(duì)尾進(jìn)行,中間不允許有任何操作,而且一旦有錯(cuò)誤的數(shù)據(jù)進(jìn)入,幾乎沒有修正的余地。雖然后進(jìn)先出的棧也不允許在中間增刪元素,但是如果將錯(cuò)誤數(shù)據(jù)壓棧,還能在下一次壓棧前及時(shí)彈棧取出錯(cuò)誤數(shù)據(jù)。隊(duì)列不提供這種機(jī)會(huì),車一旦開進(jìn)單行道,是不允許再倒出來的,只能硬著頭皮排隊(duì)。

隊(duì)列根據(jù)存儲(chǔ)方式分為兩大類型——順序隊(duì)列和鏈?zhǔn)?/strong>隊(duì)列,前者的實(shí)現(xiàn)基于數(shù)組,后者的實(shí)現(xiàn)基于鏈表。

?

2 兩種順序隊(duì)列及其代碼實(shí)現(xiàn)(Java)

順序隊(duì)列有兩種實(shí)現(xiàn)方式,一種是簡單的實(shí)現(xiàn),一種是循環(huán)的實(shí)現(xiàn)。

2.1 簡單隊(duì)列

簡單順序隊(duì)列是相對與后面要實(shí)現(xiàn)的循環(huán)順序隊(duì)列來說的,用數(shù)組來模擬單向線性的數(shù)據(jù)增刪方式,除非隊(duì)列為空,隊(duì)尾永遠(yuǎn)在隊(duì)頭之后。

2.1.1 增刪查操作的實(shí)現(xiàn)

為減少內(nèi)存的浪費(fèi)和增加使用的靈活性,我們采用帶有動(dòng)態(tài)擴(kuò)容和縮容功能的數(shù)組來實(shí)現(xiàn)。數(shù)組的第0個(gè)非空元素為隊(duì)頭元素,數(shù)組的最后一個(gè)非空元素為隊(duì)尾元素。定義一個(gè)頭指針front指向隊(duì)頭元素,尾指針rear指向隊(duì)尾元素后面的null。隊(duì)列為空時(shí),front指針和rear指針是重合的;新增數(shù)據(jù)時(shí),將數(shù)據(jù)添加到rear指針的位置,然后尾指針后移一位;刪除數(shù)據(jù)時(shí),front指針位置的值改為null,然后front指針后移一位。只要隊(duì)列不為空,rear指針一定在front指針之后。

我們在簡單順序隊(duì)列中定義了一系列用于數(shù)據(jù)增刪查的公共方法,包括:append()新增元素,pop()刪除并取回元素,remove()刪除不取回元素,peek()獲取隊(duì)頭元素,last()獲取隊(duì)尾元素,getFromFront(int distance)和getFromRear(int distance)分別是根據(jù)與隊(duì)頭和隊(duì)尾的距離查找任意位置的元素,printAll()從隊(duì)頭到隊(duì)尾遍歷并打印所有的元素。

自定義了兩個(gè)異常類,分別為空隊(duì)列異常(EmptyQueueException)和距離超出限制異常(DistanceOutOfBoundsException),分別針對刪除方法和兩個(gè)帶參數(shù)的查找方法,代碼從略。

MySimpleArrayQueue.java:準(zhǔn)備工作,創(chuàng)建一個(gè)簡單順序隊(duì)列的類

package com.notes.data_structure3;import com.notes.data_structure2.DistanceOutOfBoundsException;public class MySimpleArrayQueue<T> {// 容量縮放一次的單位為8,初始值為9,給尾指針留一個(gè)位置,因?yàn)槲仓羔樤陉?duì)尾元素之后private T[] array = (T[]) new Object[9];int front = 0; // 頭指針int rear = 0; // 尾指針/*** 從 隊(duì)尾 添加元素* @param data*/public void append(T data) {if(size()==array.length-1){expend(); // 隊(duì)列元素?cái)?shù)量達(dá)到數(shù)組容量減1,擴(kuò)容數(shù)組,減1為的是給尾指針留一個(gè)空間}array[rear++] = data; // 在尾端(尾指針位置)新增數(shù)據(jù),尾指針后移}/*** 刪除 并 取回 隊(duì)頭元素* @return* @throws EmptyQueueException*/public T pop() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}T ele = peek(); // 取出隊(duì)頭元素array[front++] = null; // 將隊(duì)頭指針位置數(shù)據(jù)改為null,然后指針后移if(array.length-size()>8) {shrink(); // 數(shù)組的空余容量超過8,縮容數(shù)組}if(isEmpty()) { // 如果數(shù)組為空,隊(duì)頭指針和隊(duì)尾指針回到0索引位置front = 0;rear = 0;}return ele;}/*** 刪除 隊(duì)頭元素 且不取回* @throws EmptyQueueException*/public void remove() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}array[front++] = null;if(array.length-size()>8) {shrink(); // 數(shù)組的空余容量超過8,縮容數(shù)組}if(isEmpty()) { // 如果數(shù)組為空,隊(duì)頭指針和隊(duì)尾指針回到0索引位置front = 0;rear = 0;}}/*** 獲取 隊(duì)頭 元素* @return* @throws EmptyQueueException*/public T peek() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}return array[front]; // 隊(duì)頭元素在頭指針的位置}/*** 獲取 隊(duì)尾 元素* @return* @throws EmptyQueueException*/public T last() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}return array[rear-1]; // 隊(duì)尾元素在尾指針的前一個(gè)位置}/*** 查找任意位置元素,按照與隊(duì)列 頭部 元素的距離* @param distance 與隊(duì)頭的距離,從隊(duì)頭開始數(shù)第幾個(gè)元素,0即隊(duì)頭本身* @return* @throws EmptyQueueException* @throws DistanceOutOfBoundsException*/public T getFromFront(int distance) throws EmptyQueueException, DistanceOutOfBoundsException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}int temp = front; // 可移動(dòng)的指針,起始位置為頭指針if(distance<size() & distance>=0) {for(int i=0;i<distance;i++) {temp++; // 指針 后移 distance次,到達(dá)指定位置}return array[temp]; // 取出指定位置的元素}else{ // distance為負(fù)值 或者 超過隊(duì)列容量的限制,報(bào)出異常throw new DistanceOutOfBoundsException("與隊(duì)列頭部的距離超出限制");}}/*** 查找任意位置元素,按照與隊(duì)列 尾部 元素的距離* @param distance 與隊(duì)尾的距離,從隊(duì)尾開始數(shù)第幾個(gè)元素,0即隊(duì)尾本身* @return* @throws EmptyQueueException* @throws DistanceOutOfBoundsException*/public T getFromRear(int distance) throws EmptyQueueException, DistanceOutOfBoundsException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}int temp = rear-1; // 起始位置為隊(duì)尾元素所在位置,即rear指針前一個(gè)位置if(distance<size() & distance>=0) {for(int i=0;i<distance;i++) {temp--; // 指針 前移 distance次,到達(dá)指定位置}return array[temp]; // 取出指定位置的元素}else{ // distance為負(fù)值 或者 超過隊(duì)列容量的限制,報(bào)出異常throw new DistanceOutOfBoundsException("與隊(duì)列尾部的距離超出限制");}}/*** 遍歷打印 隊(duì)列 中的所有元素* @throws EmptyQueueException*/public void printAll() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,報(bào)出異常throw new EmptyQueueException("隊(duì)列是空的");}for(int i=front;i<rear;i++) { // 從隊(duì)頭一直遍歷到隊(duì)尾System.out.println(array[i]);}}/*** 判斷隊(duì)列是否為空* @return 空-true,非空-false*/public Boolean isEmpty() {if(front==rear) { // 頭指針和尾指針相等,說明隊(duì)列為空return true;}return false;}/*** 計(jì)算隊(duì)列中的元素?cái)?shù)量* @return int*/public int size() {return rear-front; // 尾指針與頭指針的差值即位隊(duì)列的元素?cái)?shù)量}/*** 數(shù)組的動(dòng)態(tài)擴(kuò)容*/private void expend() {T[] newArray = (T[]) new Object[array.length + 8];System.arraycopy(array,0,newArray,0,size());array = newArray;}/*** 數(shù)組的動(dòng)態(tài)縮容*/private void shrink() {int newSize = array.length - 8;T[] newArray = (T[]) new Object[newSize];System.arraycopy(array,front,newArray,0,newSize);array = newArray;front = 0;rear = array.length;} }

SimpleArrayQueueDemo.java:模擬增刪查操作

package com.notes.data_structure3;import com.notes.data_structure2.DistanceOutOfBoundsException;public class SimpleArrayQueueDemo {public static void main(String[] args) throws EmptyQueueException, DistanceOutOfBoundsException {MySimpleArrayQueue queue = new MySimpleArrayQueue();/*** 模擬 增 的操作,從隊(duì)列 尾部 加入元素*/queue.append("榮威");queue.append("長安");queue.append("東風(fēng)");queue.append("比亞迪");queue.append("吉利");queue.append("紅旗");// queue.printAll(); // 打印驗(yàn)證/*** 模擬 刪 的操作,pop()和remove()兩種方法*/// 先進(jìn)者先出,榮威 第一個(gè)進(jìn),第一個(gè)出queue.remove();// queue.printAll(); // 打印驗(yàn)證// 將第二個(gè)進(jìn)入的 長安 取出,并讓其重新排隊(duì)String subject = (String) queue.pop();queue.append(subject);// queue.printAll(); // 打印驗(yàn)證/*** 模擬 查 的操作,peek(), last(), getFromTop(), getFromRear()* 目前隊(duì)列里的元素包括:東風(fēng) 比亞迪 吉利 紅旗 長安*/String head_ele = (String) queue.peek(); // 獲取 隊(duì)頭 元素System.out.println(head_ele); // 東風(fēng)String tail_ele = (String) queue.last(); // 獲取 隊(duì)尾 元素System.out.println(tail_ele); // 長安String ele1 = (String) queue.getFromFront(3); // 獲取中間元素,與 隊(duì)頭 的距離為3System.out.println(ele1); // 紅旗String ele2 = (String) queue.getFromRear(2); // 獲取中間元素,與 隊(duì)尾 的距離為2System.out.println(ele2); // 吉利} }

2.1.2 簡單隊(duì)列存在的弊端

以上基于帶有縮放機(jī)制的數(shù)組實(shí)現(xiàn)的隊(duì)列在設(shè)計(jì)上還是有一些不合理的地方。由于隊(duì)列先進(jìn)先出的原則,數(shù)據(jù)的增刪操作互不干擾,完全可以頻繁地交替進(jìn)行,如果程序有多個(gè)線程,增刪還可以同時(shí)進(jìn)行,就像現(xiàn)實(shí)中的單行道一樣,每時(shí)每刻都有車駛?cè)?#xff0c;有車駛出。

這樣一來,簡單實(shí)現(xiàn)方式就存在一個(gè)問題:對于隊(duì)列底層的數(shù)組,刪除操作會(huì)造成值為null的空閑空間。與此同時(shí),由于新增操作只能在末端進(jìn)行,rear指針達(dá)到數(shù)組末端后,為了加入更多數(shù)據(jù)不得不向后開辟新的空間(擴(kuò)容)。在數(shù)組觸發(fā)縮容條件之前,已經(jīng)空出來的位置不能被使用。縮容機(jī)制正是為了減少這種資源浪費(fèi),然而如果我們把縮容條件設(shè)得太緊,比如一次只縮放1個(gè)單位的容量,很容易引發(fā)過于頻繁的縮放操作,從而損耗程序的性能。

在實(shí)際應(yīng)用中,新增操作整體上是快于刪除操作的,新增的過程很簡單,就是把數(shù)據(jù)存進(jìn)去,放進(jìn)來的數(shù)據(jù)往往在等待某種處理。想象一下,如果我們的“單行道”建在一個(gè)加油站里,車輛排著隊(duì)等待加油,一輛車駛出單行道進(jìn)入加油位,后車必須等待前車加完油才能駛出,在這個(gè)過程中車輛從道路末端駛?cè)氲乃俣冗h(yuǎn)遠(yuǎn)大于加油的速度,那么我們就要不斷“擴(kuò)建”加油站的單行道,這是不現(xiàn)實(shí)的,也是不必要的。

我們給數(shù)組增加擴(kuò)容機(jī)制,目的是打破數(shù)組的申明必須指定長度所帶來的限制。其實(shí)這種限制對我們的“加油站”來說是一個(gè)很好的限流措施,加油站就那么大,單行道上只能放那么多車。采用這種思路就不能采用上面的單向線性的數(shù)據(jù)增刪方式,因?yàn)椴辉O(shè)定擴(kuò)容機(jī)制的數(shù)組會(huì)引發(fā)假溢出現(xiàn)象。比如上面的代碼所模擬的刪除與再新增操作,依次取出“榮威”和“長安”后在數(shù)組中留下兩個(gè)值為null的空閑位置,“長安”如果想重新到隊(duì)尾排隊(duì),rear指針將無所適從:

所謂假溢出,就是說對于隊(duì)列的底層實(shí)現(xiàn)數(shù)組來說已經(jīng)溢出了,rear指針失去了指向;但是對于隊(duì)列本身來說,并沒有發(fā)生溢出,因?yàn)樵氐膭h除使得隊(duì)列有了兩個(gè)空余的位置,只是不能使用。

如何讓刪除時(shí)留下的空閑位置在新增時(shí)得到再次使用,循環(huán)隊(duì)列是一個(gè)很好的解決方案。

?

2.2 循環(huán)隊(duì)列

循環(huán)隊(duì)列采用單向循環(huán)的方式組織隊(duì)列中的數(shù)據(jù),讓每一個(gè)位置都能循環(huán)使用。和簡單隊(duì)列最直觀的區(qū)別是,循環(huán)隊(duì)列的rear指針既能在front指針之后,也能在其之前。

設(shè)計(jì)循環(huán)隊(duì)列時(shí),無需為rear指針專門留出一個(gè)null位置,當(dāng)數(shù)據(jù)新增到數(shù)組的末端時(shí),如果數(shù)組被存滿,rear指針不動(dòng),同時(shí)拋出異常,以達(dá)到“限流”的目的;如果數(shù)組不滿,就意味著數(shù)組頭部有空位,rear指針移到0索引的位置,這就形成了循環(huán)

代碼實(shí)現(xiàn)時(shí),取消了數(shù)組的擴(kuò)容和縮容方法,在構(gòu)造方法中設(shè)定隊(duì)列的最大容量。由于數(shù)據(jù)的增刪操作是在隊(duì)列中循環(huán)進(jìn)行的,因此無法通過尾指針和頭指針的差值獲得隊(duì)列中的元素?cái)?shù)量,所以定義了一個(gè)count屬性在數(shù)據(jù)增刪時(shí)記錄隊(duì)列中的元素個(gè)數(shù)。front指針和rear指針重合時(shí)數(shù)據(jù)為空,由于有了count屬性,判斷隊(duì)列為空和隊(duì)列已滿的方法可以通過count實(shí)現(xiàn)。

定義的一系列增刪查的公共方法中,由于循環(huán)隊(duì)列無法通過與隊(duì)頭或隊(duì)尾的相對位置查找元素,因此取消了這組方法。隊(duì)列元素的遍歷仍然是從隊(duì)頭遍歷到隊(duì)尾,不論front指針對應(yīng)的下標(biāo)是否為0。

MyRingArrayQueue.java:準(zhǔn)備工作,創(chuàng)建一個(gè)循環(huán)順序隊(duì)列的類

package com.notes.data_structure3;public class MyRingArrayQueue<T> {private int size; // 隊(duì)列的容量private T[] array; // 定義一個(gè)數(shù)組private int front = 0; // 定義一個(gè)頭指針private int rear = 0; // 定義一個(gè)尾指針private int count = 0; // 記錄元素個(gè)數(shù)/*** 構(gòu)造方法,在此設(shè)定 隊(duì)列 的容量* 為了給rear指針留位置,數(shù)組比隊(duì)列要多一位* @param size*/public MyRingArrayQueue(int size) {this.size = size;this.array = (T[]) new Object[size+1];}/*** 添加元素,rear指針后移一位* @param data* @throws FullQueueException*/public void append(T data) throws FullQueueException {if(isFull()) { // 如果隊(duì)列已滿,拋出異常throw new FullQueueException("隊(duì)列已經(jīng)滿了");}if(rear!=size) { // 如果沒有rear指針沒有到數(shù)組最后array[rear++] = data; //在尾端(尾指針位置)新增數(shù)據(jù),尾指針后移}else {array[rear] = data; //在尾端(尾指針位置)新增數(shù)據(jù)rear = 0; // rear指針回到0索引位置,循環(huán)使用空閑位置}count++; // 隊(duì)列元素?cái)?shù)量增加一個(gè)}/*** 刪除 并 取回 元素* @return* @throws EmptyQueueException*/public T pop() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}T ele = peek(); // 取出隊(duì)頭元素array[front++] = null; // 將隊(duì)頭指針位置數(shù)據(jù)改為null,然后指針后移count--; // 隊(duì)列元素?cái)?shù)量減少一個(gè)return ele;}/*** 刪除元素且不取回* @throws EmptyQueueException*/public void remove() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}array[front++] = null; // 將隊(duì)頭指針位置數(shù)據(jù)改為null,然后指針后移count--; // 隊(duì)列元素?cái)?shù)量減少一個(gè)}/*** 獲取 隊(duì)頭 元素* @return* @throws EmptyQueueException*/public T peek() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}return array[front]; // 取出隊(duì)頭元素}/*** 獲取 隊(duì)尾 元素* @return* @throws EmptyQueueException*/public T last() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}return array[rear-1]; // 取出隊(duì)尾元素}/*** 遍歷打印 隊(duì)列 中的所有元素* @throws EmptyQueueException*/public void printAll() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}for(int i=0;i<count;i++) { // 遍歷次數(shù)為隊(duì)列中的元素個(gè)數(shù)if(front<=size) { // 先從 隊(duì)頭 遍歷到 數(shù)組末端System.out.println(array[front++]);}else { // 如果還有遍歷次數(shù),再從 數(shù)組頭部 開始遍歷int temp = 0;System.out.println(array[temp++]);}}}/*** 判斷隊(duì)列是否為空* @return*/public Boolean isEmpty() {if(count==0) {return true;}return false;}/*** 判斷隊(duì)列是否已滿* @return*/public Boolean isFull() {if(count==size) {return true;}return false;}/*** 返回當(dāng)前隊(duì)列的元素?cái)?shù)量* @return*/public int getCount() {return count;} }

RingArrayQueueDemo.java:模擬增刪查操作

package com.notes.data_structure3;public class RingArrayQueueDemo {public static void main(String[] args) throws FullQueueException, EmptyQueueException {/*** 設(shè)定 隊(duì)列 容量,“加油站”最多駛?cè)?6 輛車*/MyRingArrayQueue queue = new MyRingArrayQueue(6);/*** 模擬 增 的操作,從隊(duì)列 尾部 加入元素*/queue.append("榮威");queue.append("長安");queue.append("東風(fēng)");queue.append("比亞迪");queue.append("吉利");queue.append("紅旗");// queue.printAll(); // 打印驗(yàn)證/*** 模擬 刪 的操作,pop()和remove()兩種方法*/// 先進(jìn)者先出,榮威 第一個(gè)進(jìn),第一個(gè)出queue.remove();// queue.printAll(); // 打印驗(yàn)證// 將第二個(gè)進(jìn)入的 長安 取出,并讓其重新排隊(duì)String subject = (String) queue.pop();queue.append(subject);// queue.printAll(); // 打印驗(yàn)證// 再新增一個(gè) 奇瑞queue.append("奇瑞");// queue.printAll(); // 打印驗(yàn)證/*** 模擬 查 的操作,peek(), last()* 目前隊(duì)列里的元素包括:東風(fēng) 比亞迪 吉利 紅旗 長安 奇瑞*/String head_ele = (String) queue.peek(); // 獲取 隊(duì)頭 元素System.out.println(head_ele); // 東風(fēng)String tail_ele = (String) queue.last(); // 獲取 隊(duì)尾 元素System.out.println(tail_ele); // 奇瑞} }

?

3 鏈?zhǔn)疥?duì)列及其代碼實(shí)現(xiàn)(Java)

3.1 鏈?zhǔn)疥?duì)列的設(shè)計(jì)思路

鏈?zhǔn)疥?duì)列顧名思義就是基于鏈表實(shí)現(xiàn)的隊(duì)列,用Java實(shí)現(xiàn)鏈?zhǔn)疥?duì)列的方法和實(shí)現(xiàn)單向鏈表的方法是非常相似的,參見單向鏈表的筆記,鏈接在下面:

https://blog.csdn.net/weixin_45370422/article/details/116573863

之所以能夠用實(shí)現(xiàn)單向鏈表的方法實(shí)現(xiàn)鏈?zhǔn)疥?duì)列,源于我們對鏈?zhǔn)疥?duì)列的設(shè)計(jì)思路。在我們的設(shè)想中,隊(duì)列的rear指針指向最新進(jìn)入隊(duì)列的元素,front指針指向不存數(shù)據(jù)的頭結(jié)點(diǎn),頭結(jié)點(diǎn)指向隊(duì)頭元素。設(shè)計(jì)頭結(jié)點(diǎn)的目的是:當(dāng)隊(duì)列為空時(shí),指針仍有所指向,不至于成為“野指針”。

鏈?zhǔn)疥?duì)列和順序隊(duì)列一樣,當(dāng)隊(duì)列為空時(shí),front指針和rear指針的指向相同;和順序隊(duì)列不同的是,順序隊(duì)列給rear指針留了一個(gè)空位,鏈?zhǔn)疥?duì)列與之相反,給front指針留了一個(gè)空位。

回顧用Java模擬單向鏈表的過程,我們定義了一個(gè)頭結(jié)點(diǎn)(headNode)。在鏈?zhǔn)疥?duì)列的設(shè)想中,需要一個(gè)外部指針front指向這個(gè)頭結(jié)點(diǎn)。然而,指針其實(shí)也是一個(gè)結(jié)點(diǎn)對象,那么為了簡化代碼,完全可以讓front指針自己充當(dāng)頭結(jié)點(diǎn),front.next指向隊(duì)頭結(jié)點(diǎn),即讓頭指針的指針指向隊(duì)頭。同樣地,單向鏈表中還定義了一個(gè)當(dāng)前結(jié)點(diǎn)(currentNode),而當(dāng)且結(jié)點(diǎn)正相當(dāng)于鏈?zhǔn)疥?duì)列中rear指針要指向的最新進(jìn)入的元素,那么為了簡化代碼,也完全可以讓rear指針自己充當(dāng)隊(duì)尾結(jié)點(diǎn),rear.next指向null,即隊(duì)尾(鏈尾)的指針是空指針,新增結(jié)點(diǎn)時(shí),rear指針后移一位。

這樣一來,我們就可以用實(shí)現(xiàn)單向鏈表的方法來實(shí)現(xiàn)鏈?zhǔn)疥?duì)列,增刪操作分別通過調(diào)節(jié)front的指針(next)和rear的指針(next)來實(shí)現(xiàn)。基于隊(duì)列不能在中間增刪元素的原則,我們?nèi)∠藛蜗蜴湵碇卸x的在中間插入和刪除元素的方法。

3.2 增刪查操作的實(shí)現(xiàn)

MyLinkedQueue.java:準(zhǔn)備工作,創(chuàng)建一個(gè)鏈?zhǔn)疥?duì)列的類

package com.notes.data_structure3;import com.notes.data_structure2.DistanceOutOfBoundsException;public class MyLinkedQueue<T> {private Node front = new Node(null); // 隊(duì)頭指針private Node rear = front; // 隊(duì)尾指針private int count; // 用于統(tǒng)計(jì)隊(duì)列中的元素?cái)?shù)量// 定義一個(gè)結(jié)點(diǎn)類public class Node {// 結(jié)點(diǎn)的兩個(gè)要素:數(shù)據(jù)和指針private T data;private Node next;// 構(gòu)造方法,初始化data屬性public Node(T data) {this.data = data;}@Overridepublic String toString() { // 可供printAll()方法調(diào)用return "Node{" +"data=" + data +'}';}}/*** 向 隊(duì)尾 增加元素* @param data*/public void append(T data) {Node node = new Node(data);if(isEmpty()) { // 如果隊(duì)列為空,front指針充當(dāng)頭結(jié)點(diǎn)front.next = node; // front的指針指向隊(duì)頭元素}rear.next = node; // rear的指針指向新結(jié)點(diǎn)rear = node; // 新結(jié)點(diǎn)為rear結(jié)點(diǎn),正式加入隊(duì)列,rear實(shí)現(xiàn)后移count++; // 隊(duì)列元素?cái)?shù)量增加一個(gè)}/*** 從 隊(duì)頭 刪除元素 并取回* @return*/public T pop() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}T ele = peek(); // 取出 隊(duì)頭 元素front.next = front.next.next; // front的指針指向隊(duì)頭元素的下一個(gè)元素count--; // 隊(duì)列元素?cái)?shù)量減少一個(gè)return ele;}/*** 從 隊(duì)頭 刪除元素 不取回*/public void remove() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}front.next = front.next.next; // front的指針指向隊(duì)頭元素的下一個(gè)元素count--; // 隊(duì)列元素?cái)?shù)量減少一個(gè)}/*** 獲取 隊(duì)頭 元素* @return* @throws EmptyQueueException*/public T peek() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}return (T) front.next.data; // 返回隊(duì)頭結(jié)點(diǎn)的數(shù)據(jù)值}/*** 獲取 隊(duì)尾 元素* @return* @throws EmptyQueueException*/public T last() throws EmptyQueueException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}return rear.data; // 返回隊(duì)尾結(jié)點(diǎn)的數(shù)據(jù)值}/*** 查找任意位置元素,按照與隊(duì)列 頭部 元素的距離* @param distance 與隊(duì)頭的距離,從隊(duì)頭開始數(shù)第幾個(gè)元素,0即隊(duì)頭本身* @return* @throws EmptyQueueException* @throws DistanceOutOfBoundsException*/public T getFromFront(int distance) throws EmptyQueueException, DistanceOutOfBoundsException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");}if(distance<count & distance>=0){Node temp = front; // 可移動(dòng)的指針,起始位置為頭結(jié)點(diǎn)for(int i=0;i<distance+1;i++) {temp = temp.next; // 指針 后移 distance+1 次,到達(dá)指定位置}return (T) temp.data; // 取出指定位置的元素}else { // distance為負(fù)值 或者 超過隊(duì)列容量的限制,報(bào)出異常throw new DistanceOutOfBoundsException("與隊(duì)頭的距離超出限制");}}/*** 查找任意位置元素,按照與隊(duì)列 尾部 元素的距離* @param distance 與隊(duì)尾的距離,從隊(duì)尾開始數(shù)第幾個(gè)元素,0即隊(duì)尾本身* @throws EmptyQueueException* @throws DistanceOutOfBoundsException*/public T getFromRear(int distance) throws EmptyQueueException, DistanceOutOfBoundsException {if(isEmpty()) { // 如果隊(duì)列為空,拋出異常throw new EmptyQueueException("隊(duì)列是空的");} // 根據(jù)與隊(duì)尾的距離計(jì)算出與隊(duì)頭的距離,再調(diào)用getFromFront()方法return getFromFront(count-1-distance);}/*** 打印全部元素*/public void printAll() {Node temp = front; // 可移動(dòng)的指針,起始位置為頭結(jié)點(diǎn)while (temp.next!=null) {temp = temp.next; // 不斷后移,直到nullSystem.out.println(temp);}}/*** 判斷隊(duì)列是否為空* @return*/public Boolean isEmpty() {if(rear==front) { // 如果隊(duì)頭和隊(duì)尾指向相同,隊(duì)列為空return true;}return false;}/*** 隊(duì)列當(dāng)前的元素個(gè)數(shù)* @return*/public int size() {return count;} }

LinkedQueueDemo.java:模擬增刪查操作

package com.notes.data_structure3;import com.notes.data_structure2.DistanceOutOfBoundsException;public class LinkedQueueDemo {public static void main(String[] args) throws EmptyQueueException, DistanceOutOfBoundsException {MyLinkedQueue queue = new MyLinkedQueue();/*** 模擬 增 的操作,從 隊(duì)頭 加入元素,即 “先進(jìn)”*/queue.append("榮威");queue.append("長安");queue.append("東風(fēng)");queue.append("比亞迪");queue.append("吉利");queue.append("紅旗");// queue.printAll(); // 打印驗(yàn)證/*** 模擬 刪 的操作,pop()和remove()兩種方法*/// 先進(jìn)者先出,榮威 第一個(gè)進(jìn),第一個(gè)出queue.remove();// queue.printAll(); // 打印驗(yàn)證// 將第二個(gè)進(jìn)入的 長安 取出,并讓其重新排隊(duì)String subject = (String) queue.pop();queue.append(subject);// queue.printAll(); // 打印驗(yàn)證/*** 模擬 查 的操作,peek(), last(), getFromTop(), getFromRear()* 目前隊(duì)列里的元素包括:東風(fēng) 比亞迪 吉利 紅旗 長安*/String head_ele = (String) queue.peek(); // 獲取 隊(duì)頭 元素System.out.println(head_ele); // 東風(fēng)String tail_ele = (String) queue.last(); // 獲取 隊(duì)尾 元素System.out.println(tail_ele); // 長安String ele1 = (String) queue.getFromFront(3); // 獲取中間元素,與 隊(duì)頭 的距離為3System.out.println(ele1); // 紅旗String ele2 = (String) queue.getFromRear(2); // 獲取中間元素,與 隊(duì)尾 的距離為2System.out.println(ele2); // 吉利} }

?

總結(jié)

以上是生活随笔為你收集整理的数据结构学习笔记(三):队列(queue)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。