1. 程式人生 > >教你如何使用Java手寫一個基於連結串列的佇列

教你如何使用Java手寫一個基於連結串列的佇列

  在上一篇部落格【教你如何使用Java手寫一個基於陣列的佇列】中已經介紹了佇列,以及Java語言中對佇列的實現,對佇列不是很瞭解的可以我上一篇文章。那麼,現在就直接進入主題吧。

  這篇部落格主要講解的是如何使用單鏈表實現一個簡單版的佇列。單向連結串列佇列是屬於非迴圈佇列,同時佇列的長度是不受限制的,也就是說新增資料的速度比拉取資料的速度快時,佇列的長度是無限增長的。單鏈佇列其本質就是一個連結串列,只不過是在獲取或新增資料的時候跟普通的連結串列有所區別,佇列在獲取資料的同時也將該節點刪除,並且每次獲取資料都是從表頭獲取,普通連結串列可以獲取任意節點的資料;佇列在增加資料時總是新增到連結串列的尾部,而普通的連結串列則是可以在連結串列的任意地方插入一個節點。

 

  一、佇列資料結構

  該佇列的結構夠多個節點構成,每個節點都有一個指向下一個節點的指標,使得所有的節點都能夠相連,形成一個連結串列。具體結構如下如:

  Java實現:

static class Node{
        Object item;
        Node next;

        public Node(Object item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

  其實就建立一個靜態內部類即可,類中item是用來儲存資料的,next是指向下一個node節點,最後一個節點的next為空。

 

  二、屬性

  head:連結串列表頭,拉取或遍歷資料都是從這裡開始的。

  tail:連結串列表尾,每次新增資料都新增到tail的後面,變成新的tail

  size:佇列長度

  //表頭
    private Node head;
    //表尾
    private Node tail;
    //佇列長度
    private int size;

 

  三、新增資料

 public void offer(Object item){
        Node n = new Node(item,null);
        
if (tail == null){ head = tail = n; }else { tail.next = n; tail = n; } size++; }

  用程式碼實現佇列的新增操作看起是很簡單的,無非就是將新節點新增到表尾,然後把新節點設定成新的表尾。

 

  四、拉取資料

public Object poll(){
        if (head == null) return null;
        Node h = head;
        //將拉取的節點的下一個節點變成新的表頭
        head = head.next;
        //把舊的表頭的下一個節點指向設定為null,讓gc回收
        h.next = null;
        //佇列為空
        if (head == null)
            tail = null;
        size--;
        return h.item;
    }

  

  五、檢視資料

public Object peek(){
        return head == null ? null : head.item;
    }

  檢視資料看的是表頭的資料,但是跟poll方法的區別是該方法不會刪除表頭的資料。

 

  六、清空列表

public void clear(){
        size = 0;
        Node h = head;
        while (h != null){
            Node temp = h.next;
            h.next = null;
            h = temp;
        }
        head = tail = null;
    }

  

  七、基於陣列的佇列和連結串列的佇列的區別

  1、前者是有邊界的迴圈佇列,後者則是沒有邊界的非迴圈佇列。

  2、前者在新增資料時無需建立新物件,效能消耗相對較小,後者每次新增資料都需要建立新物件。

  3、後者每個節點都維護了一個鏈,所以所需記憶體也相對較大。

  4、如果新增速度大於拉取速度,前者在達到邊界後可能會無法新增資料,後者則沒有這個問題。

 

  八、完整程式碼

/**
 * 基於連結串列實現的佇列
 */
public class LinkQueue {

    //表頭
    private Node head;
    //表尾
    private Node tail;
    //佇列長度
    private int size;

    public LinkQueue(){}

    public void offer(Object item){
        Node n = new Node(item,null);
        if (tail == null){
            head = tail = n;
        }else {
            tail.next = n;
            tail = n;
        }
        size++;
    }

    public Object poll(){
        if (head == null) return null;
        Node h = head;
        //將拉取的節點的下一個節點變成新的表頭
        head = head.next;
        //把舊的表頭的下一個節點指向設定為null,讓gc回收
        h.next = null;
        //佇列為空
        if (head == null)
            tail = null;
        size--;
        return h.item;
    }

    public Object peek(){
        return head == null ? null : head.item;
    }

    public void clear(){
        size = 0;
        Node h = head;
        while (h != null){
            Node temp = h.next;
            h.next = null;
            h = temp;
        }
        head = tail = null;
    }

    public int size(){return size;}

    static class Node{
        Object item;
        Node next;

        public Node(Object item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    @Override
    public String toString() {
        if (size == 0) return "{}";
        StringBuilder builder = new StringBuilder(size + 2);
        Node h = head;
        builder.append("{");
        while (h != null){
            builder.append(h.item);
            builder.append(", ");
            h = h.next;
        }
        return builder.substring(0,builder.length() -2) + "}";
    }
}