數(shù)據(jù)結(jié)構(gòu)-隊(duì)列

數(shù)據(jù)結(jié)構(gòu)-隊(duì)列

定義

隊(duì)列(queue)在計(jì)算機(jī)科學(xué)中杭攻,是一種先進(jìn)先出的線(xiàn)性表狮腿。
它只允許在表的前端進(jìn)行刪除操作,而在表的后端進(jìn)行插入操作。進(jìn)行插入操作的端稱(chēng)為隊(duì)尾辣垒,進(jìn)行刪除操作的端稱(chēng)為隊(duì)頭。隊(duì)列中沒(méi)有元素時(shí)源葫,稱(chēng)為空隊(duì)列雄坪。

基于自定義數(shù)組實(shí)現(xiàn)的隊(duì)列

新建queue接口,用來(lái)規(guī)范所有queue子類(lèi)

package com.datastructure.queue;

import java.awt.*;

/**
 * @program: test
 * @description:
 * @author: Mr.Yang
 * @create: 2019-05-03 16:44
 **/
public class LoopQueue<E> implements Queue<E> {

    private E[] data;

    //指向隊(duì)列的第一個(gè)元素匹厘,初始指向0
    private int front;

    //指向隊(duì)列的最后一個(gè)元素的后一個(gè)位置嘀趟,初始指向0
    private int tail;

    private int size;

    public LoopQueue(int capacity){
        data = (E[]) new Object[capacity+1];
        front=0;
        tail=0;
        size=0;
    }

    public LoopQueue(){
        this(10);
    }

    /**
     * 因?yàn)槿萘糠诺臅r(shí)候多了個(gè)1,所以get容量的時(shí)候愈诚,需要減1
     * @return
     */
    public int getCapacity(){
        return data.length-1;
    }


    /**
     * 1.if((tail + 1) % data.length == front) 如果tail + 1 超過(guò)了data.length的大小她按,
     *   代表當(dāng)前tail指向已經(jīng)超出了容量的大小牛隅,因?yàn)槭茄h(huán)式,所以需要tail去循環(huán)頭元素中查看值是否有被占用尤溜,
     *   如果 == front 代表循環(huán)頭沒(méi)有倔叼,就需要擴(kuò)容了。
     * 2.舉例: 元素容量為8宫莱,tail目前指向7 front 指向2
     *          if((7 + 1) % 8 == 2 )  if(0 == 2) 這里是false丈攒,因?yàn)閒ront指向了2,所以代表 第0,1位是沒(méi)有值的
     *          所以這個(gè)值需要在在第0位放(空間利用)
     * 3.data[tail] = param  tail當(dāng)前指向的地方需要賦值授霸,然后tail自增 循環(huán)體 的1巡验,size+1
     * @param param
     */
    @Override
    public void enqueue(E param) {
        if((tail + 1) % data.length == front){
            resize(getCapacity() * 2);
        }
        data[tail] = param ;
        tail = (tail + 1) % data.length;
        size ++ ;
    }

    /**
     * 擴(kuò)充隊(duì)列的容量
     * 1.front代表了當(dāng)前元素初始位置的指向
     * 2.newData的第i位元素,應(yīng)該等于 i + front % data.length 的值
     * 3.舉例:元素容量20碘耳,i 等于 0 显设,front 等于 2,結(jié)果: newData[0] = data[(0 + 2) %  20]
     *         = data[2]   意思就是辛辨,newData的第一位元素捕捂,應(yīng)該等于data有值的第一位元素
     *         % data.length 的原因主要是為了防止數(shù)組越界錯(cuò)誤
     * 4.新數(shù)組賦值完成需要將 front 重新指向0,因?yàn)樾聰?shù)組的front指針是從0開(kāi)始的斗搞。
     *   tail最后要指向等于size大小的值指攒,
     * @param newCapacity
     */
    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity + 1];
        for(int i = 0 ; i < size ; i++){
            newData[i] = data[(i + front ) % data.length];
        }
        data=newData;
        front = 0 ;
        tail = size;
    }

    /**
     * 1.如果隊(duì)列為空拋出異常
     * 2.用ret變量來(lái)接受當(dāng)前隊(duì)列頭的值
     * 3.接收成功之后將,隊(duì)列頭元素置空
     * 4.front指針指向下一個(gè)元素
     * 5.size大小-1
     * 6.如果size大小占據(jù)了容量的1/4和size為容量的1/2且不等于0的時(shí)候僻焚,對(duì)容量進(jìn)行縮減允悦,縮減為原來(lái)容量的1/2
     * 7.返回ret變量
     * @return
     */
    @Override
    public E dequeue() {
        if(isEmpty()){
            throw new IllegalArgumentException("dequeue is fail ,because queue is empty");
        }
        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("queue is empty");
        }
        return data[front];
    }

    @Override
    public int getSize() {
        return size;
    }

    /**
     * 當(dāng)front和tail的值相等時(shí),隊(duì)列為空虑啤,初始兩個(gè)指向的是同一個(gè)值(只有初始的時(shí)候隙弛,指向的是同一個(gè)地方)
     * @return
     */
    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    /**
     * 1.元素從 front位置開(kāi)始循環(huán)遍歷,i的值不能等于tail狞山,
     *   也就是到tail的前一位全闷,i = i + 1 且%data.length,
     *   因?yàn)閕有可能從循環(huán)頭重新開(kāi)始
     * 2.( i + 1 ) % data.length != tail  如果當(dāng)前i + 1 % data.length
     *   不等于tail表示不到最后一個(gè)元素萍启,就拼接室埋,
     * @return
     */
    @Override
    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append(String.format("Array: size = %d , capacity = %d\n", size, getCapacity()));
        sb.append("front [");
        for (int i = front; i != tail ; i = (i + 1) % data.length) {
            sb.append(data[i]);
            if (( i + 1 ) % data.length != tail) {
                sb.append(", ");
            }
        }
        sb.append("] tail");
        return sb.toString();
    }
}

新建ArrayQueue實(shí)現(xiàn)類(lèi)

package com.datastructure.queue;

import com.datastructure.array.Array;

/**
 * @program: test
 * @description:
 * @author: Mr.Yang
 * @create: 2019-05-03 18:19
 **/
public class ArrayQueue<E> implements Queue<E>{

    Array<E> array;


    public ArrayQueue(int capacity){
        array=new Array<E>(capacity);
    }



    public ArrayQueue(){
        array=new Array<E>();
    }


    @Override
    public void enqueue(E param) {
        array.addLast(param);
    }

    @Override
    public E dequeue() {
        return array.removeFirst();
    }

    @Override
    public E getFront() {
        return array.getFirst();
    }

    @Override
    public int getSize() {
        return array.getSize();
    }

    @Override
    public boolean isEmpty() {
        return array.isEmpty();
    }

    @Override
    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append("front: ");
        sb.append("[");
        for(int i=0;i<array.getSize();i++){
            sb.append(array.get(i));
            if(i!=array.getSize()-1){
                sb.append(", ");
            }
        }
        sb.append("]  tail");
        return sb.toString();
    }
}

測(cè)試類(lèi)

package com.datastructure.queue;

/**
 * @program: test
 * @description:
 * @author: Mr.Yang
 * @create: 2019-05-03 18:26
 **/
public class QueueTest {

    public static void main(String[] args) {
        ArrayQueue<Integer> integerArrayQueue = new ArrayQueue<>();
        for (int i = 0; i < 10; i++) {
            integerArrayQueue.enqueue(i);
            System.out.println(integerArrayQueue);


            if(i % 3 == 2){
                integerArrayQueue.dequeue();
                System.out.println(integerArrayQueue);
            }
        }
    }

}

測(cè)試結(jié)果

front: [0]  tail
front: [0, 1]  tail
front: [0, 1, 2]  tail
front: [1, 2]  tail
front: [1, 2, 3]  tail
front: [1, 2, 3, 4]  tail
front: [1, 2, 3, 4, 5]  tail
front: [2, 3, 4, 5]  tail
front: [2, 3, 4, 5, 6]  tail
front: [2, 3, 4, 5, 6, 7]  tail
front: [2, 3, 4, 5, 6, 7, 8]  tail
front: [3, 4, 5, 6, 7, 8]  tail
front: [3, 4, 5, 6, 7, 8, 9]  tail

可以看到測(cè)試結(jié)果是正確的,也符合隊(duì)列結(jié)構(gòu)的數(shù)據(jù)存取伊约,但是因?yàn)槭腔谧远x數(shù)組來(lái)實(shí)現(xiàn)的姚淆,所以會(huì)調(diào)用數(shù)組方法的removeFirst方法,刪除第一個(gè)元素的同時(shí)屡律,會(huì)重新將后面所有元素前移腌逢,索引前移,均攤時(shí)間復(fù)雜度為O(n)超埋。

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

循環(huán)隊(duì)列中有兩個(gè)新詞搏讶,兩個(gè)指針

  • front 指向隊(duì)列的第一個(gè)元素佳鳖,初始指向0
  • tail 指向隊(duì)列的最后一個(gè)元素的后一個(gè)位置,初始指向0
a.gif

建立一個(gè)loopqueue實(shí)現(xiàn)queue接口

package com.datastructure.queue;

import java.awt.*;

/**
 * @program: test
 * @description:
 * @author: Mr.Yang
 * @create: 2019-05-03 16:44
 **/
public class LoopQueue<E> implements Queue<E> {

    private E[] data;

    //指向隊(duì)列的第一個(gè)元素媒惕,初始指向0
    private int front;

    //指向隊(duì)列的最后一個(gè)元素的后一個(gè)位置系吩,初始指向0
    private int tail;

    private int size;

    public LoopQueue(int capacity){
        data = (E[]) new Object[capacity+1];
        front=0;
        tail=0;
        size=0;
    }

    public LoopQueue(){
        this(10);
    }

    /**
     * 因?yàn)槿萘糠诺臅r(shí)候多了個(gè)1,所以get容量的時(shí)候妒蔚,需要減1
     * @return
     */
    public int getCapacity(){
        return data.length-1;
    }


    /**
     * 1.if((tail + 1) % data.length == front) 如果tail + 1 超過(guò)了data.length的大小穿挨,
     *   代表當(dāng)前tail指向已經(jīng)超出了容量的大小,因?yàn)槭茄h(huán)式肴盏,所以需要tail去循環(huán)頭元素中查看值是否有被占用科盛,
     *   如果 == front 代表循環(huán)頭沒(méi)有,就需要擴(kuò)容了菜皂。
     * 2.舉例: 元素容量為8贞绵,tail目前指向7 front 指向2
     *          if((7 + 1) % 8 == 2 )  if(0 == 2) 這里是false,因?yàn)閒ront指向了2恍飘,所以代表 第0,1位是沒(méi)有值的
     *          所以這個(gè)值需要在在第0位放(空間利用)
     * 3.data[tail] = param  tail當(dāng)前指向的地方需要賦值榨崩,然后tail自增 循環(huán)體 的1,size+1
     * @param param
     */
    @Override
    public void enqueue(E param) {
        if((tail + 1) % data.length == front){
            resize(getCapacity() * 2);
        }
        data[tail] = param ;
        tail = (tail + 1) % data.length;
        size ++ ;
    }

    /**
     * 擴(kuò)充隊(duì)列的容量
     * 1.front代表了當(dāng)前元素初始位置的指向
     * 2.newData的第i位元素章母,應(yīng)該等于 i + front % data.length 的值
     * 3.舉例:元素容量20蜡饵,i 等于 0 ,front 等于 2胳施,結(jié)果: newData[0] = data[(0 + 2) %  20]
     *         = data[2]   意思就是,newData的第一位元素肢专,應(yīng)該等于data有值的第一位元素
     *         % data.length 的原因主要是為了防止數(shù)組越界錯(cuò)誤
     * 4.新數(shù)組賦值完成需要將 front 重新指向0舞肆,因?yàn)樾聰?shù)組的front指針是從0開(kāi)始的。
     *   tail最后要指向等于size大小的值博杖,
     * @param newCapacity
     */
    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity + 1];
        for(int i = 0 ; i < size ; i++){
            newData[i] = data[(i + front ) % data.length];
        }
        data=newData;
        front = 0 ;
        tail = size;
    }

    /**
     * 1.如果隊(duì)列為空拋出異常
     * 2.用ret變量來(lái)接受當(dāng)前隊(duì)列頭的值
     * 3.接收成功之后將椿胯,隊(duì)列頭元素置空
     * 4.front指針指向下一個(gè)元素
     * 5.size大小-1
     * 6.如果size大小占據(jù)了容量的1/4和size為容量的1/2且不等于0的時(shí)候,對(duì)容量進(jìn)行縮減剃根,縮減為原來(lái)容量的1/2
     * 7.返回ret變量
     * @return
     */
    @Override
    public E dequeue() {
        if(isEmpty()){
            throw new IllegalArgumentException("dequeue is fail ,because queue is empty");
        }
        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("queue is empty");
        }
        return data[front];
    }

    @Override
    public int getSize() {
        return size;
    }

    /**
     * 當(dāng)front和tail的值相等時(shí)哩盲,隊(duì)列為空,初始兩個(gè)指向的是同一個(gè)值(只有初始的時(shí)候狈醉,指向的是同一個(gè)地方)
     * @return
     */
    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    /**
     * 1.元素從 front位置開(kāi)始循環(huán)遍歷廉油,i的值不能等于tail,
     *   也就是到tail的前一位苗傅,i = i + 1 且%data.length抒线,
     *   因?yàn)閕有可能從循環(huán)頭重新開(kāi)始
     * 2.( i + 1 ) % data.length != tail  如果當(dāng)前i + 1 % data.length
     *   不等于tail表示不到最后一個(gè)元素,就拼接渣慕,
     * @return
     */
    @Override
    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append(String.format("Array: size = %d , capacity = %d\n", size, getCapacity()));
        sb.append("front [");
        for (int i = front; i != tail ; i = (i + 1) % data.length) {
            sb.append(data[i]);
            if (( i + 1 ) % data.length != tail) {
                sb.append(", ");
            }
        }
        sb.append("] tail");
        return sb.toString();
    }
}

測(cè)試代碼

package com.datastructure.queue;

/**
 * @program: test
 * @description:
 * @author: Mr.Yang
 * @create: 2019-05-03 18:26
 **/
public class QueueTest {



    public static void main(String[] args) {
        LoopQueue<Integer> integerArrayQueue = new LoopQueue<>();
        for (int i = 0; i < 10; i++) {
            integerArrayQueue.enqueue(i);
            System.out.println(integerArrayQueue);


            if(i % 3 == 2){
                integerArrayQueue.dequeue();
                System.out.println(integerArrayQueue);
            }
        }
    }
}

測(cè)試結(jié)果

Array: size = 1 , capacity = 10
front [0] tail
Array: size = 2 , capacity = 10
front [0, 1] tail
Array: size = 3 , capacity = 10
front [0, 1, 2] tail
Array: size = 2 , capacity = 5
front [1, 2] tail
Array: size = 3 , capacity = 5
front [1, 2, 3] tail
Array: size = 4 , capacity = 5
front [1, 2, 3, 4] tail
Array: size = 5 , capacity = 5
front [1, 2, 3, 4, 5] tail
Array: size = 4 , capacity = 5
front [2, 3, 4, 5] tail
Array: size = 5 , capacity = 5
front [2, 3, 4, 5, 6] tail
Array: size = 6 , capacity = 10
front [2, 3, 4, 5, 6, 7] tail
Array: size = 7 , capacity = 10
front [2, 3, 4, 5, 6, 7, 8] tail
Array: size = 6 , capacity = 10
front [3, 4, 5, 6, 7, 8] tail
Array: size = 7 , capacity = 10
front [3, 4, 5, 6, 7, 8, 9] tail

打印結(jié)果跟自定義數(shù)組的結(jié)果是一樣的嘶炭,但是因?yàn)橐昧酥羔樳@個(gè)概念抱慌,刪除的時(shí)候索引不會(huì)重排,均攤時(shí)間復(fù)雜度為O(1)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眨猎,一起剝皮案震驚了整個(gè)濱河市抑进,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌睡陪,老刑警劉巖寺渗,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宝穗,居然都是意外死亡户秤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)逮矛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸡号,“玉大人,你說(shuō)我怎么就攤上這事须鼎【ò椋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵晋控,是天一觀的道長(zhǎng)汞窗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)赡译,這世上最難降的妖魔是什么仲吏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蝌焚,結(jié)果婚禮上裹唆,老公的妹妹穿的比我還像新娘。我一直安慰自己只洒,他們只是感情好许帐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著毕谴,像睡著了一般成畦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涝开,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天循帐,我揣著相機(jī)與錄音,去河邊找鬼舀武。 笑死惧浴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奕剃。 我是一名探鬼主播衷旅,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼捐腿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了柿顶?” 一聲冷哼從身側(cè)響起茄袖,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嘁锯,沒(méi)想到半個(gè)月后宪祥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡家乘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蝗羊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仁锯。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耀找,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出业崖,到底是詐尸還是另有隱情野芒,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布双炕,位于F島的核電站狞悲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妇斤。R本人自食惡果不足惜摇锋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望站超。 院中可真熱鬧荸恕,春花似錦、人聲如沸顷编。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)媳纬。三九已至,卻和暖如春施掏,著一層夾襖步出監(jiān)牢的瞬間钮惠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工七芭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留素挽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓狸驳,卻偏偏與公主長(zhǎng)得像预明,于是被迫代替她去往敵國(guó)和親缩赛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容