1. 程式人生 > >java實現佇列MyQueue,底層使用連結串列實現

java實現佇列MyQueue,底層使用連結串列實現

/**
 * 佇列的介面
 * 佇列是一種先進先出的資料結構
 * 佇列支援的操作:
 * 判斷佇列是否為空
 * 判斷佇列是否已經滿了
 * 檢視佇列已經有多少元素
 * 將一個元素入隊
 * 將一個元素出隊
 * 檢視隊頭的元素,但不出隊
 * 佇列在底層可以用陣列實現,也可以用連結串列實現
 * 但不管實現方式如何,都必須滿足此介面中的規定
 */
public interface MyQueue<Item> extends Iterable<Item>{
    /**
     * 判斷佇列是否為空
     * @return 如果佇列為空,則返回true;否則,返回false
     */
    public boolean isEmpty();

    /**
     * 判斷佇列是否為滿,滿了返回true,否則返回false
     * @return
     */
    public boolean isFull();

    /**
     * 返回佇列中元素的個數
     * @return 個數
     */
    public int size();

    /**
     * 入隊,將一個元素入隊
     * @param item 待入隊的元素
     * @return 入隊成功返回true,否則返回false
     */
    public boolean enqueue(Item item);

    /**
     * 將一個元素出隊
     * @return  出隊成功返回出隊的元素,否則返回null
     */
    public Item dequeue();

    /**
     * 檢視佇列頭部的元素,但不出隊
     * @return 佇列頭部的元素,如果沒有元素,返回null
     */
    public Item get();
}
/**
 * 底層使用連結串列實現佇列,而且這個版本帶頭結點
 * 初始化的時候,指標front和rear都指向頭結點
 * 入隊時,讓新的結點插在表尾
 * 出隊時,刪除頭結點之後的那個結點
 * 用這種方法實現佇列最容易出錯的地方是在只剩下一個元素的時候出隊,
 * 因為此時要改變rear的值,讓它重新指向頭結點
 * @param <Item>
 */
public class LinkedQueueWithHeadNode<Item> implements MyQueue<Item> {
    private Node front;
    private Node rear;
    private int size;
    private class Node{
        Item item;
        Node next;
    }

    /**
     * 建構函式初始化佇列,生成新的結點,並且讓front和rear都指向它
     * size=0
     */
    public LinkedQueueWithHeadNode() {
        front=new Node();
        front.item=null;    //頭結點中的資料域是null,當然預設也是null
        front.next=null;
        rear=front;
        size=0;
    }

    @Override
    public boolean isEmpty() {
        return front==rear;
    }

    /**
     * 鏈式的佇列理論上不會滿,這也是鏈式的優點
     * @return
     */
    @Override
    public boolean isFull() {
        return false;
    }

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

    @Override
    public boolean enqueue(Item item) {
        Node newNode =new Node();
        newNode.item=item;
        newNode.next=null;
        rear.next=newNode;
        rear=newNode;
        size++;
        return true;
    }

    /**
     * 出隊的邏輯比較複雜
     * 如果連結串列有兩個及以上的元素,那麼就屬於正常情況
     * 刪除頭結點之後的那個結點就可以完成出隊
     * 然而如果連結串列就只有一個元素,即頭結點和之後的一個結點
     * 那麼在刪除完頭結點後面那個結點後,還需要維護rear結點,
     * 讓rear結點重新指向頭結點
     * @return
     */
    @Override
    public Item dequeue() {
        if(isEmpty()){
            return null;
        }
        Node p=front.next;
        Item old=p.item;
        front.next=p.next;
        if(p==rear){
            rear=front;
        }
        size--;
        return old;
    }

    @Override
    public Item get() {
        if(isEmpty()){
            return null;
        }
        return front.next.item;
    }

    @Override
    public Iterator<Item> iterator() {
        return new LinkedQueueIterator1();
    }

    private class LinkedQueueIterator1 implements Iterator<Item>{
        private Node p=front;
        @Override
        public boolean hasNext() {
            return p!=rear;
        }

        @Override
        public Item next() {
            Item item=p.next.item;
            p=p.next;
            return item;
        }
    }
}

測試:

public class LinkedQueueTest1 {
    public static void main(String[] args) {
        MyQueue<Integer> myQueue=new LinkedQueueWithHeadNode<>();
        myQueue.enqueue(1);
        myQueue.enqueue(2);
        myQueue.enqueue(13);
        myQueue.enqueue(20);
        myQueue.enqueue(35);
        myQueue.enqueue(50);
        myQueue.enqueue(70);
        System.out.println("size is "+myQueue.size());
        System.out.println("刪除了"+myQueue.dequeue());
        System.out.println("刪除了"+myQueue.dequeue());
        System.out.println("刪除了"+myQueue.dequeue());
        System.out.println("刪除了"+myQueue.dequeue());
        myQueue.enqueue(100);
        System.out.println("size is "+myQueue.size());
        System.out.println("next is "+myQueue.get());
        for (int i:myQueue){
            System.out.println(i);
        }
    }
}

結果:

size is 7
刪除了1
刪除了2
刪除了13
刪除了20
size is 4
next is 35
35
50
70
100

以上是帶頭結點的鏈式佇列,接下來是不帶頭結點的鏈式佇列的實現。

/**
 * 不帶頭結點的連結串列
 * 初始的時候,front和rear都為null
 * 入隊的時候,要判斷是否是從空表到插入第一個結點(這是一個特殊的情況)
 * 正常情況是表非空,那麼就是正常插入到連結串列的尾部
 * 出隊的時候,要判斷是剩下唯一一個結點被刪除(這是一個特出的情況)
 * 如果還是有很多結點,那麼從表頭刪除即可
 * 由於不帶頭結點,所以預設的建構函式即可
 * @param <Item>
 */
public class LinkedQueueWithoutHeadNode<Item> implements MyQueue<Item> {
    private Node front;
    private Node rear;
    private int size;
    private class Node{
        Item item;
        Node next;
    }
    @Override
    public boolean isEmpty() {
        return front==null;
    }

    /**
     * 鏈式佇列,所以理論上不會滿
     * @return
     */
    @Override
    public boolean isFull() {
        return false;
    }

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

    @Override
    public boolean enqueue(Item item) {
        Node newNode =new Node();
        newNode.item=item;
        newNode.next=null;
        if(rear==null){
            rear=newNode;
        }
        rear.next=newNode;
        rear=newNode;
        if(front==null){
            front=rear;
        }
        size++;
        return true;
    }

    @Override
    public Item dequeue() {
        if(isEmpty()){
            return null;
        }
        Item old=front.item;
        front=front.next;
        if(front==null){
            rear=null;
        }
        size--;
        return old;
    }

    @Override
    public Item get() {
        if (isEmpty()){
            return null;
        }
        return front.item;
    }

    @Override
    public Iterator<Item> iterator() {
        return new LinkedQueueIterator2();
    }

    private class LinkedQueueIterator2 implements Iterator<Item>{
        private Node cur=front;
        @Override
        public boolean hasNext() {
            return cur!=null;
        }

        @Override
        public Item next() {
            Item item=cur.item;
            cur=cur.next;
            return item;
        }
    }
}

測試:

public class LinkedQueueTest2 {
    public static void main(String[] args) {
        MyQueue<Integer> myQueue=new LinkedQueueWithoutHeadNode<>();
        myQueue.enqueue(1);
        myQueue.enqueue(2);
        myQueue.enqueue(13);
        myQueue.enqueue(20);
        myQueue.enqueue(35);
        myQueue.enqueue(50);
        myQueue.enqueue(70);
        System.out.println("size is "+myQueue.size());
        System.out.println("刪除了"+myQueue.dequeue());
        System.out.println("刪除了"+myQueue.dequeue());
        System.out.println("刪除了"+myQueue.dequeue());
        System.out.println("刪除了"+myQueue.dequeue());
        myQueue.enqueue(100);
        System.out.println("size is "+myQueue.size());
        System.out.println("next is "+myQueue.get());
        for (int i:myQueue){
            System.out.println(i);
        }
    }
}

結果:

size is 7
刪除了1
刪除了2
刪除了13
刪除了20
size is 4
next is 35
35
50
70
100

到此為止,佇列的java基本實現已經全部完畢。 總結: 佇列可以在底層用陣列和連結串列來實現。 陣列實現一般採用迴圈陣列,因為不會浪費空間。 陣列的實現只要控制好了索引的位置,實現的方式可以不同。 具體實現看上一篇部落格: https://blog.csdn.net/Yangziqi_usst/article/details/83657054 而採用連結串列來實現佇列,理論上佇列不會滿。鏈式佇列分為帶頭結點和不帶頭結點。 OVER