看得見的數據結構Android版之隊列篇

零、前言

1.現實世界裏咱們更多講究的是先來後到,先排隊先買票,這樣纔有秩序,畢竟咱們沒有計算機那麼有耐心
2.使用隊列結構能很好的模擬和解決相似生活中的事,好比消息的發送用隊列維護就是很是恰當的
3.隊列就像去動物園買票,先處理隊列的頭部,有新的人來了就後面排着去,慢慢等
4.還有一種頗有意思的隊列是循環隊列,它是因爲數組對頭部操做的困難性,從而轉變一種思路,讓數組也能很好的實現隊列結構,後面會仔細分析一下
5.本例操做演示源碼:但願你能夠和我在Github一同見證:DS4Android的誕生與成長,歡迎stargit

1.留圖鎮樓:隊列的最終實現的操做效果:
數組實現普通隊列:

藍色區域是數組看見:初始化四個空間,不夠再擴容,空閒太多再縮容github

數組實現普通隊列.gif

鏈表實現普通隊列:

隊列綜合操做.gif


2.隊列結構的簡介:
隊列是一種線性的數據結構  
特性:尾部添加,頭部取出 即先進先出FIFO  
操做:enqueue入隊  dequeue出隊  getFront查看隊首元素
複製代碼

隊列.png


1、隊列接口

兵馬未動,糧草先行,有接口好辦事。編程

/**
 * 做者:張風捷特烈
 * 時間:2018/8/17 0017:15:57
 * 郵箱:1981462002@qq.com
 * 說明:隊列接口
 */
public interface IQueue<T> {
    /**
     * 入隊
     * @param el 元素
     */
    void enqueue(T el);

    /**
     * 出隊
     * @return 元素
     */
    T dequeue();

    /**
     * 獲取隊首元素
     * @return 隊首元素
     */
    T getFront();

    /**
     * 獲取隊列元素個數
     * @return 元素個數
     */
    int getSize();

    /**
     * 是否爲空
     * @return 是否爲空
     */
    boolean isEmpty();
}
複製代碼

2、普通隊列的數組實現

普通隊列的數組實現----性能很是差,後面用數組實現循環隊列來優化
爲何會不好,由於尾添加和頭刪除,總有一個會讓全部的人挪一挪,後面會用數組實現循環隊列來優化數組

/**
 * 做者:張風捷特烈
 * 時間:2018/8/17 0017:15:57
 * 郵箱:1981462002@qq.com
 * 說明:普通隊列的數組實現----性能很是差,後面用數組實現循環隊列來優化
 */
public class ArrayChartQueue<E> implements IQueue<E> {
    private ArrayChart<E> array;//成員變量

    public ArrayChartQueue(int capacity) {
        this.array = new ArrayChart<>(capacity);
    }

    public ArrayChartQueue() {
        this.array = new ArrayChart<>();
    }

    @Override
    public void enqueue(E el) {
        array.add(el);
    }

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

    @Override
    public E front() {
        return array.get(0);
    }

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

    @Override
    public boolean isEmpty() {
        return array.isEmpty();
    }
}
複製代碼
2.數組普通隊列的插入演示:

因爲是基於數組來實現,因此一切的操做也是基於數組
初始四個大小的數組,就像招待處預留四把椅子,而後等椅子坐滿了,再來加椅子bash

數組普通隊列的插入.gif

3.數組普通隊列的查看首元演示:

數組普通隊列查看首元.gif

4.數組普通隊列的移除演示:

數組表結構移除頭部...萬惡之源,千萬不要用,此處僅演示! 此處僅演示!此處僅演示!!
數組表結構移除頭部...萬惡之源,千萬不要用,此處僅演示! 此處僅演示!此處僅演示!!微信

數組普通隊列移除首元.gif


3、循環隊列

基於數組實現的隊列在隊首取出時會使得整隊移動,效率會很低
可是壯哉我大數組,豈會連個小小的隊列都搞不定,之後還哪還有臉立足王座...因而循環隊列出現了
提及循環你們腦子裏都是一個圈來回轉,循環小數表示不服... 只要有週期性就是循環,想成一個圈就狹隘了數據結構

1.循環隊列實現的思路:
不就是想要知道隊尾和隊首是那個嘛,我標出來,維護一下給你不就好了嗎  
注意:這裏的優點在於維護了隊尾和隊首的標示,插入尾和刪除頭都是定點,並且數組總體不移動,而是標示在動
新加元素時,隊尾表識後移,不夠就擴容。刪除頭時隊首標示  

循環隊列特色:
爲空時,`隊尾標示==隊首標示`,
隊列滿:`(隊尾標示+1)%數組長度==隊首標示`   
循環隊列會使隊首前一個位置不可用。
複製代碼

循環隊列.png

循環隊列循環機制.png

/**
 * 做者:張風捷特烈
 * 時間:2018/8/17 0017:16:03
 * 郵箱:1981462002@qq.com
 * 說明:數組實現循環隊列
 */
public class ArrayLoopQueue<T> implements IQueue<T> {
    private T[] data;// 隊列數據
    private int head;//隊首標示
    private int tail;//隊尾標示
    private int size;//元素個數
    public ArrayLoopQueue() {//無參構造:默認8個容量
        this(8);
    }
    public ArrayLoopQueue(int capacity) {
        // 由於會有一個浪費,因此+1
        data = (T[]) new Object[capacity + 1];
        head = 0;
        tail = 0;
        size = 0;
    }
    @Override
    public void enqueue(T el) {
        if (isFull()) {//加入時滿了---擴容
            grow(capacity() * 2);
        }
        data[tail] = el;//在隊尾插入
        //插入數據時對尾標示進行維護-----
        tail = (tail + 1) % data.length;
        size++;
    }
    @Override
    public T dequeue() {
        if (isEmpty()) {
            throw new IllegalArgumentException("MakeSure it's not an empty } T ret = data[head]; data[head] = null;//讓隊首移除 //隊首移除時對首標示進行維護----- head = (head + 1) % data.length; size--; //閒置太多---縮容 if (size == capacity() / 4 && capacity() / 2 != 0 && size > 4) { grow(capacity() / 2); } return ret; } @Override public T front() { if (isEmpty()) { throw new IllegalArgumentException("MakeSure it's not an empty } return data[head]; } /** * 擴容/縮容 * * @param newCapacity 新的容量 */ private void grow(int newCapacity) { T[] newData = (T[]) new Object[newCapacity + 1]; for (int i = 0; i < size; i++) { // 此時在newData中隊首對齊回來,data中就得有一個front的偏移量 newData[i] = data[(i + head) % data.length]; } data = newData; head = 0; tail = size; } /** * 獲取容量 * * @return 容量 */ public int capacity() { return data.length - 1; } /** * 隊列元素個數 * * @return 元素個數 */ @Override public int size() { return size; } /** * 是否爲空 * * @return 是否爲空 */ @Override public boolean isEmpty() { return head == tail; } /** * 隊列是否滿了 * * @return 隊列是否滿了 */ private boolean isFull() { // tail的下一個位置等於head時 return (tail + 1) % data.length == head; } } 複製代碼

4、單鏈表式實現隊列結構

鏈表和隊列可謂自然配,鏈表的頭刪除,頭獲取很快,但尾添加要獲取尾部,須要遍歷一次,很差
但能夠維護首位標識,使隊尾也容易獲取。(固然你也能夠用雙鏈表...直接批件衣服,改都不用改)
註釋的很清楚了,看着代碼順一下,或debug走一波,我就不贅述了ide

/**
 * 做者:張風捷特烈
 * 時間:2018/8/17 0017:22:50
 * 郵箱:1981462002@qq.com
 * 說明:單鏈表實現隊列
 */
public class SingleLinkedQueue<T> implements IQueue<T> {

    private Node head;//頭節點
    private Node tail;//尾節點

    private int size;//元素個數

    public SingleLinkedQueue() {
        head = null;
        tail = null;
        size = 0;
    }

    @Override
    public void enqueue(T el) {//入隊
        // 若是隊尾爲空,說明隊列是空的。由於tail一直指向最後一個非空節點。
        if (tail == null) {
            tail = new Node(null, el);//初始化
            head = tail;
        } else {
            tail.next = new Node(null, el); // 新來的排到後面去
            tail = tail.next; //更新隊尾
        }
        size++;
    }

    @Override
    public T dequeue() {//出隊
        if (isEmpty())
            throw new IllegalArgumentException("MakeSure it's not an empty queue");
        Node targetNode = head;//我是老大
        head = head.next; // 我是老二,但我要篡位了...之後哥就是老大
        targetNode.next = null; //前任老大走了....
        if (head == null) {// 若是頭結點爲空
            tail = null;
        }
        size--;
        return targetNode.el;
    }

    @Override
    public T front() {
        if (isEmpty())
            throw new IllegalArgumentException("MakeSure it's not an empty queue");
        return head.el;
    }

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

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    private class Node {
        private T el;//改節點上的元素
        private Node next; //下一節點
        /**
         * 兩參構造
         *
         * @param next //下一節點
         * @param el   生成節點的元素值
         */
        private Node(Node next, T el) {
            this.el = el;
            this.next = next;
        }
    }
}
複製代碼

5、小結

1.數組普通隊列:ArrayChartQueue測試
方法\數量 1000 次10000次 10W次 100W次 1000次
enqueue 0.0006秒 0.0022秒 0.01571秒 0.06668秒 1.1375秒
dequeue 0.0111秒 0.2707秒 18.7684秒 ---- --
2.數組環形隊列:ArrayLoopQueue測試
方法\數量 1000 次10000次 10W次 100W次 1000次
enqueue 0.0004秒 0.0019秒 0.01775秒 0.05414秒 0.6896秒
dequeu 0.0005秒 0.0021秒 0.0091秒 0.0360秒 0.3327秒
3.鏈表隊列:SingleLinkedStack測試
方法\數量 1000 次10000次 10W次 100W次 1000次
enqueue 0.0011秒 0.0031秒 0.0099秒 0.4881秒 3.1186秒
dequeue 0.0002秒 0.0013秒 0.0046秒 0.0221秒 0.1388秒

可見循環隊列仍是蠻好的,壯哉,我大數組!
數組普通隊列,就認識一下吧...不要用它。
數組環形隊列和鏈表隊列的比較也就至關於數組和鏈表的比較oop


本系列後續更新連接合集:(動態更新)

後記:捷文規範

2.本文成長記錄及勘誤表
項目源碼 日期 備註
V0.1--github 2018-11-24 看得見的數據結構Android版之隊列結構的實現
3.更多關於我
筆名 QQ 微信 愛好
張風捷特烈 1981462002 zdl1994328 語言
個人github 個人簡書 個人掘金 我的網站
4.聲明

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持post


icon_wx_200.png
相關文章
相關標籤/搜索