以前總結過棧相關的知識,隊列能夠類比棧來看。棧只能在一端進行操做(棧頂),添加元素或者刪除等都只能在棧頂;而隊列有兩端能夠操做,在一端添加元素,在另外一端刪除元素。html
咱們把添加元素的一端稱爲隊尾;刪除元素的一端稱爲隊首。java
好比生活中的排隊:城市中基本哪裏都有,這就是一個隊列。在隊伍最前面就是隊首,也是最早完成離開隊伍的。新來的只能在隊尾加入。數組
用鏈表、數組都比較容易實現隊列。this
鏈表實現:比較簡單,在鏈表首部刪除,鏈表尾部插入便可。
鏈表沒有長度限制,不會形成溢出,並且插入刪除也很簡單,是很適合的實現方式。code
數組實現:普通數組方式,有着明顯的缺陷,長度固定、出隊時剩下全部元素都要向前挪動一位,這種出隊方式時間複雜度就是O(n)了。在此基礎上進行一些處理,造成一個環,成爲一個循環隊列。htm
因此下面主要介紹的就是兩種隊列實現,用鏈表實現的隊列 和 用數組實現的循環隊列。blog
隊列主要兩個操做,入隊和出隊。
入隊(enQueue): 即在隊尾插入新元素。
出隊(deQueue): 即在隊首刪除一個元素。
若是本身實現,能夠添加更多公共操做,如隊長、是否隊空或隊滿、查找等等。隊列
下面實現中,爲了更直觀的顯示出隊、入隊操做,都添加了一個displayQueue()的方法。ci
單鏈表隊列實現很簡單,鏈表首部即隊首,鏈表尾部即隊尾,定義兩個引用分別指向隊首和隊尾,出隊、入隊直接刪除和插入便可。時間複雜度爲O(1)。
優點,沒有額外的操做,沒有長度的限制 不需擔憂溢出。get
示意圖大體以下:
具體代碼以下(可結合註釋,應該比較清晰):
public class QueueLinkedList<E> { public static void main(String[] args) { //入隊1,2,3,4,5;而後出隊2次 QueueLinkedList<Integer> queueLinkedList = new QueueLinkedList<Integer>(); queueLinkedList.displayQueue(); for (int i = 1; i < 6; i++) { queueLinkedList.enQueue(Integer.valueOf(i)); } queueLinkedList.displayQueue(); queueLinkedList.deQueue(); queueLinkedList.displayQueue(); queueLinkedList.deQueue(); queueLinkedList.displayQueue(); } //定義隊首引用,指向鏈表首部 private QueueNode<E> front; //定義隊尾引用,指向鏈表尾部 private QueueNode<E> rear; //節點定義 static class QueueNode<E> { E data; QueueNode<E> next; public QueueNode(E data) { this.data = data; } } //初始化,空的隊列。 public QueueLinkedList() { this.front = this.rear = null; } //入隊,鏈表尾部插入 public void enQueue(E value) { QueueNode<E> newNode = new QueueNode<E>(value); //空隊列時 if (this.rear == null) { this.front = this.rear = newNode; return; } this.rear.next = newNode; this.rear = newNode; } //出隊,鏈表首部刪除 public E deQueue() { //隊列爲空時 if (this.front == null) return null; E result = this.front.data; this.front = this.front.next; //隊列中只有一個元素時 if (this.front == null) this.rear = null; return result; } //打印隊列全部元素,以及隊首、隊尾信息 public void displayQueue() { if (this.front == null) { System.out.println("front:null<-...<-rear:null"); System.out.println("This is an empty queue!"); System.out.println(); return; } System.out.println("front:" + this.front.data +"<-...<-rear:" + this.rear.data); QueueNode<E> tmpNode = this.front; while (tmpNode != null) { System.out.print(tmpNode.data + "<-"); tmpNode = tmpNode.next; } System.out.println(); System.out.println(); } }
實驗結果爲:
front:null<-...<-rear:null This is an empty queue! front:1<-...<-rear:5 1<-2<-3<-4<-5<- front:2<-...<-rear:5 2<-3<-4<-5<- front:3<-...<-rear:5 3<-4<-5<-
若用普通數組實現隊列,就會發現每次刪除隊首時全部元素都須要向前移動一位,這樣實現確定是不合適的。
因而,在上述基礎上作一些處理,便造成了循環隊列:將數組隊列看出一個首尾相連的環形。用front和rear表示隊首和隊尾相關元素的下標(不必定是front是隊首下標,rear是隊尾下表),這樣就能很容易肯定隊列的元素是哪些了。出隊、入隊改變這兩個值便可而不須要移動數組中元素了。
下面實現用變量front和rear標識隊首、隊尾的下標,入隊和出隊的方式基本一致:數組大小爲capacity,即front=(font+1)%capacity 或 rear=(rear+1)%capacity。
也能夠用front標識隊首,rear標識隊尾的下一個位置,只是不一樣的標識含義, 初始化 和 隊列爲空 及 隊列爲滿 的判斷和處理須要注意,是不一樣的。 (rear標識隊尾下一個位置時,初始化front=rear=0,空即front==rear。而rear標識隊尾即下面的實現,能夠具體看下)
大體示意圖以下(front和rear標識隊首、隊尾的下標):
實現代碼(可結合註釋查看):
注:實現中的判空與判滿 使用了另外一個變量queueCount表示隊列元素個數,便於返回隊列大小。也可使用front和rear之間關係進行判斷
public class QueueCircleArray { public static void main(String[] args) { //隊列入隊1,2,3,4;而後兩次出隊;而後入隊10,20 QueueCircleArray queueCircleArray = new QueueCircleArray(4); queueCircleArray.displayQueue(); for (int i = 1; i < 5; i++) { queueCircleArray.enQueue(Integer.valueOf(i)); } queueCircleArray.displayQueue(); queueCircleArray.deQueue(); queueCircleArray.deQueue(); queueCircleArray.displayQueue(); queueCircleArray.enQueue(Integer.valueOf(10)); queueCircleArray.enQueue(Integer.valueOf(20)); queueCircleArray.displayQueue(); } //數組及大小 private Object[] array; private int capacity; //隊首 隊尾對應數組中的下標 private int front, rear; //隊列的大小 private int queueCount; public QueueCircleArray() { this(10); } public QueueCircleArray(int capacity) { this.capacity = capacity; this.front = queueCount = 0; //注意這裏初始化 this.rear = this.capacity - 1; this.array = new Object[this.capacity]; } public boolean isFull() { return this.queueCount == this.capacity; } public boolean isEmpty() { return this.queueCount == 0; } public void enQueue(Object value) { if (isFull()) throw new RuntimeException("Queue is Full!"); //隊尾向後移動一位,即將插入數據的位置 this.rear = (this.rear+1)%this.capacity; //插入數據,入隊 this.array[this.rear] = value; //隊列大小加1 this.queueCount++; } public Object deQueue() { if (isEmpty()) throw new RuntimeException("Queue is Empty!"); Object tmpObject = this.array[this.front]; //隊首向後移動一位,以前的位置即出隊的元素。 this.front = (this.front+1)%this.capacity; //隊列大小減1 this.queueCount--; return tmpObject; } public void displayQueue() { if (isEmpty()) { System.out.println("front:null<-...<-rear:null"); System.out.println("This is an empty queue!"); System.out.println(); return; } int tmp = this.front; System.out.println("front:["+ this.front + "]" + this.array[this.front] +"<-...<-rear:[" + this.rear + "]" + this.array[this.rear]); while(tmp != this.rear) { System.out.print(this.array[tmp] + "<-"); tmp = (tmp + 1)%this.capacity; } if (tmp == this.rear) System.out.println(this.array[tmp]); System.out.println(); } }
實驗結果:
front:null<-...<-rear:null This is an empty queue! front:[0]1<-...<-rear:[3]4 1<-2<-3<-4 front:[2]3<-...<-rear:[3]4 3<-4 front:[2]3<-...<-rear:[1]20 3<-4<-10<-20