1. 程式人生 > >資料結構(四)佇列

資料結構(四)佇列

一、基本概念

1、特點:

  • 在佇列頭部進行刪除,在佇列的尾部進行插入操作

2、主要實現:

  • 使用迴圈陣列
  • 使用連結串列
    在這裡插入圖片描述
    3、關係圖:
    在這裡插入圖片描述

二、Queue

public interface Queue<E> extends Collection<E> {
  //新增	
  boolean add(E var1);
  //新增
  boolean offer(E var1);
  //移除
  E remove();
  //移除
  E poll();
  //獲取元素
  E element();
  //獲取
  E peek();
}

一般peek、poll、offer方法跟其他類似方法的區別在於為空會返回null

三、Deque

1、特點:

  • 雙端佇列:Deque是一種具有佇列和棧的性質的資料結構,插入和刪除操作可以在表的兩端進行。
    LinkedList實現了Deque介面,可以在兩端進行插入和刪除操作。

2、介面原始碼:

public interface Deque<E> extends Queue<E> {
    //將元素插入佇列
	boolean add(E e);
	//將元素插入佇列,與add相比,在容量受限時應該使用這個
	boolean offer(E e);
	//將隊首的元素刪除,佇列為空則丟擲異常
	E remove();
	//將隊首的元素刪除,佇列為空則返回null
	E poll();
	//獲取隊首元素,但不移除,佇列為空則丟擲異常
	E element();
	//獲取隊首元素,但不移除,佇列為空則返回null
	E peek();
	...
}

提供了在兩端移除、新增的相關方法

四、ArrayDeque

雙端佇列

  • ArrayDeque通過迴圈陣列的方式實現迴圈佇列,容量為2的次冪。
  • 作為Stack,因為其非執行緒安全所以效率高於java.util.Stack,而作為佇列,因為其不需要結點支援所以更快。

1、變數:

public class ArrayDeque<E> extends AbstractCollection<E>
        implements Deque<E>, Cloneable, Serializable {
    //元素儲存為陣列
    transient Object[] elements;
    //頭部元素索引
    transient int head;
    //隊部元素索引
    transient int tail;
    //最小容量
    private static final int MIN_INITIAL_CAPACITY = 8;
    ...
}

2、構造:

/**
 * 無參構造,預設大小為16
 */
public ArrayDeque() {
    elements = new Object[16];
}
/**
 * @param numElements 初始容量
 */
public ArrayDeque(int numElements) {
    allocateElements(numElements);
}
/**
 * 傳入一個集合
 * @param c
 */
public ArrayDeque(Collection<? extends E> c) {
	//獲取初始容量,並建立陣列
    allocateElements(c.size());
	//新增元素
    addAll(c);
}

獲取初始容量,並建立陣列

private void allocateElements(int numElements) {
    int initialCapacity = MIN_INITIAL_CAPACITY;
    //如果傳入的容量>=MIN_INITIAL_CAPACITY
    if (numElements >= initialCapacity) {
	    //通過右移和位或運算得到大於numElements的2^n個容量
        initialCapacity = numElements;
        initialCapacity |= (initialCapacity >>>  1);
        initialCapacity |= (initialCapacity >>>  2);
        initialCapacity |= (initialCapacity >>>  4);
        initialCapacity |= (initialCapacity >>>  8);
        initialCapacity |= (initialCapacity >>> 16);
        initialCapacity++;

        if (initialCapacity < 0)    // Too many elements, must back off
            initialCapacity >>>= 1; // Good luck allocating 2^30 elements
    }
    elements = new Object[initialCapacity];
}

3、新增元素

新增元素都是從佇列的尾部新增,如果呼叫addFirst,就是將索引為0作為隊首,需要從length-1的位置(隊尾)開始新增。如果addLast,則索引為0作為隊尾。

public void addFirst(E e) {
    if (e == null)
        throw new NullPointerException();
    //如果elements.length為8,最終head的索引為7,然後賦值
    elements[head = (head - 1) & (elements.length - 1)] = e;
    //進行擴容
    if (head == tail)
        doubleCapacity();
}
//如果將1-8的元素依次通過addFirst新增到佇列中,此時head是從7到0,最終的順序為[8,7,6,5,4,3,2,1]。

public void addLast(E e) {
    if (e == null)
        throw new NullPointerException();
    //隊尾賦值,首次新增元素時tail為0
    elements[tail] = e;
	//將tail後移一位
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        doubleCapacity();
}
//如果將1-8的元素依次通過addLast新增到佇列中,此時tail從1到8,最終的順序為[1,2,3,4,5,6,7,8]

//將容量擴大到2倍,然後元素複製到新陣列
private void doubleCapacity() {
     assert head == tail;
     int p = head;
     int n = elements.length;
     int r = n - p; // number of elements to the right of p
     int newCapacity = n << 1;
     if (newCapacity < 0)
         throw new IllegalStateException("Sorry, deque too big");
     Object[] a = new Object[newCapacity];
     System.arraycopy(elements, p, a, 0, r);
     System.arraycopy(elements, 0, a, r, p);
     elements = a;
     head = 0;
     tail = n;
 }

4、刪除元素:

刪除元素時在隊首刪除,removeFirst時隊首在前(索引為head)開始刪,removeLast時隊首在後(索引為tail)處開始刪。

public E removeFirst() {
    E x = pollFirst();
    if (x == null)
        throw new NoSuchElementException();
    return x;
}

public E removeLast() {
    E x = pollLast();
    if (x == null)
        throw new NoSuchElementException();
    return x;
}

public E pollFirst() {
    final Object[] elements = this.elements;
    final int h = head;
    @SuppressWarnings("unchecked")
    //獲取head元素
    E result = (E) elements[h];
    // Element is null if deque empty
    if (result != null) {
	    //head的元素置空,將head向後移動一位
        elements[h] = null; // Must null out slot
        head = (h + 1) & (elements.length - 1);
    }
    return result;
}

public E pollLast() {
    final Object[] elements = this.elements;
    //通過&運算獲取隊首元素索引(如果tail為2,隊首索引t為1)
    final int t = (tail - 1) & (elements.length - 1);
    @SuppressWarnings("unchecked")
    E result = (E) elements[t];
    if (result != null) {
	    //刪除最後一個元素,將tail向前移動一位
        elements[t] = null;
        tail = t;
    }
    return result;
}

5、獲取元素:

public E getFirst() {
    @SuppressWarnings("unchecked")
    E result = (E) elements[head];
    if (result == null)
        throw new NoSuchElementException();
    return result;
}

public E getLast() {
    @SuppressWarnings("unchecked")
    E result = (E) elements[(tail - 1) & (elements.length - 1)];
    if (result == null)
        throw new NoSuchElementException();
    return result;
}