Java資料資料結構(三)——佇列
今天但看了大二資料結構這本書,對佇列進行一個整理。
文章目錄
一、什麼是佇列
佇列和棧一樣,都是一種受限制的線性表。佇列元素只能從隊尾插入(稱為入隊),隊首刪除(稱為出隊),就像排隊買奶茶,作為一名有素質的中國人,新來的人會自動地排在隊伍的後面,隊伍前面的人會先買到奶茶。這就是所謂的先進先出(First In First Out)。佇列實現的方式有兩種基本結構:陣列和連結串列。我們想要實現佇列的基本操作包括:
//入隊 //出隊 //獲取隊首元素 //獲取長度 //是否為空 //是否為滿 //顯示佇列元素
二、順序陣列實現佇列
假設有這樣一個佇列,隊首處於陣列下標為0的位置,隊尾在陣列後端,那麼顯然入隊的時候就是在隊尾新增元素,時間複雜度為O(1),但是出隊的時候要刪除Array[0]的元素,陣列後面的元素需要全部往前移動,時間複雜度為O(n),顯然,如果把隊尾放在順序陣列的位置0處,那麼出隊的時間複雜度為O(1),入隊的時間複雜度為O(n),如圖所示:
針對上述的問題,我們可以新增兩個指標front(隊首),rear(隊尾),在入隊和出隊的操作只需要移動兩個指標的位置,而無需移動佇列中的元素,將時間複雜度都降為O(1)。
你以為這樣就完事了嗎?當然不是,陣列一個固有的特點,就是一旦建立之後陣列的大小就固定不變了,如果我們不斷地進行入隊和出隊操作,就會出現一系列問題,佇列滿了,陣列越界了!隨著front的值不斷增加,陣列前面的一大片空間就浪費了,記憶體是不是要哭死,哈哈!所以下面就會講述關於迴圈陣列實現佇列的方法來處理上述的弊端。
三、迴圈陣列實現佇列
使用網上優秀的圖,說明一下判斷佇列空和滿的方法。
程式碼實現:
public CircleQueue() { maxSize = 4; front = 0; rear = 0; dataArr =(E[])new Object[maxSize]; } //入隊 public void enqueue(Object data) { if(isFull()) { System.out.println("佇列滿了!!!"); }else { dataArr[rear] = data; rear = (rear+1)%maxSize; } } //出隊 public Object dequeue() { if(isEmpty()) { System.out.println("佇列空了!!!"); return null; }else { Object o = dataArr[front]; front = (front+1)%maxSize; return o; } } //獲取佇列長度 public int length() { return (rear-front+maxSize)%maxSize; } //判斷是否為空 public boolean isEmpty() { return rear == front; } //判斷是否為滿 public boolean isFull() { return (rear+1)%maxSize == front; } public void print() { if(length()==0) { System.out.println("佇列元素為空"); }else { System.out.println("佇列元素為:"); for(int i=front;i<=length();i++) { if(dataArr[i] != null) { System.out.println(dataArr[i]); } } } System.out.println("rear="+rear); System.out.println("front="+front); }
測試程式碼:
public static void main(String[] args) {
CircleQueue<Object> queue = new CircleQueue<>();
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(4);
System.out.println("佇列長度= "+queue.length());
queue.print();
queue.enqueue(5);
System.out.println("佇列長度= "+queue.length());
queue.print();
queue.dequeue();
queue.print();
}
測試結果:
四、連結串列實現佇列
利用連結串列實現佇列,可以動態地建立結點,不用預設大小,也不會造成空間的浪費,整個佇列的記憶體空間不連續,在實現插入和刪除操作更容易實現,但是存取速度會慢。鏈式佇列的實現就是對連結串列做了簡單的修改,根據佇列的特點,我們可以選用雙端連結串列的形式,建立兩個指標front,rear指向連結串列頭結點和尾結點。
同樣實現上述的基本操作。
package com.lm.MyQueue1121;
public class QueueLinklist {
private Node front;
private Node rear;
public class Node{
private Node next;
private Object data;
public Node(Object data) {
this.data = data;
}
}
public QueueLinklist() {
front = null;
rear = null;
}
//入隊
public void enqueue(Object value) {
Node node = new Node(value);
if(isEmpty()) {//注意插入第一個節點,要給front賦值
rear = node;
front = node;
}else {
rear.next = node;
rear = node;
}
}
//出隊
public Object dequeue() {
if(isEmpty()) {
System.out.println("佇列為空!!!");
return null;
}else {
Object o = front.data;
front = front.next;
System.out.println(o+" 出佇列");
return o;
}
}
//獲取長度
public int length() {
Node node = front;
int len = 0;
while(node != null) {
len++;
node = node.next;
}
return len;
}
//判斷是否為空
public boolean isEmpty() {
return length()==0;
}
public void print() {
Node node = front;
if(node == null) {
System.out.println("佇列為空!!!");
}
while(node != null) {
System.out.println("佇列元素:"+node.data);
node = node.next;
}
}
public static void main(String[] args) {
QueueLinklist queue = new QueueLinklist();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.print();
System.out.println("佇列長度="+queue.length());
queue.dequeue();
queue.dequeue();
queue.print();
System.out.println("佇列長度="+queue.length());
}
}
測試結果: