@
1、佇列的定義和特點
和上一篇的棧相反,佇列(queue)是一種先進先出(First In First Out, FIFO)的線性表。
它只允許在表的一端進行插入,而在另一端刪除元素。這和日常生活中的排隊是一致的,最早進入佇列的元素最早離開。
在佇列中,允許插入的一端稱為隊尾(rear), 允許 刪除的一端則稱為隊頭(front)。出佇列和入佇列示意圖如下:
2、佇列的基本操作
佇列的基本運算和堆疊類似,包含判空、獲取長度、入隊、出隊、出隊、取隊頭(不刪除隊頭)等。
我們這裡定義一個佇列的介面。
/**
* @Author 三分惡
* @Date 2020/8/27
* @Description 佇列
*/
public interface Queue {
public boolean isEmpty(); //判空
public int size(); //獲取佇列容量
public void enQueue(Object element); //入隊
public Object deQueue(); //出隊
public Object getHead(); //取隊首元素
}
佇列是一種受限的線性表,同樣有順序和鏈式兩種實現。
3、順序佇列
這裡順序佇列通過可擴容陣列來實現。
在類裡標記了隊頭和對尾的下標。
入隊時,隊尾往後移動,隊頭保持不變,出隊是隊頭往後移動,隊尾保持不變。
為什麼不保持隊頭指向0?因為如果隊首指向0,那麼出隊的時候需要將陣列前移,時間複雜度為O(n)。使用了隊頭和隊尾標記之後,出隊時隊頭往後移動一位,這樣避免了元素的移動。
/**
* @Author 三分惡
* @Date 2020/8/27
* @Description 順序佇列
*/
public class ArrayQueue implements Queue{
private static int defaultSize=15; //預設容量
private int size; //實際容量:實際儲存元素個數
private Object[] data; //存放元素的陣列
private int front=0; //隊頭(下標)
private int rear=0; //隊尾(下標)
/**
* 無參構造方法:按預設容量構造元素陣列
*/
public ArrayQueue(){
data=new Object[defaultSize];
}
/**
* 有參構造方法:指定元素陣列容量
* @param capacity
*/
public ArrayQueue(int capacity){
data=new Object[capacity];
}
/**
* 判空
* @return
*/
public boolean isEmpty() {
return size==0;
}
/**
* 獲取佇列元素個數
* @return
*/
public int size() {
return size;
}
/**
* 入隊
* @param element
*/
public void enQueue(Object element) {
//如果隊滿
if (size==data.length&&front==0){
//真隊滿,擴容
if (front==0){
//擴容兩倍的新陣列
Object [] newData=new Object[size<<1];
//拷貝陣列
System.arraycopy(data,0,newData,0,size);
data=newData;
}else{
//假隊滿:前移元素
//所有資料前移front位
for (int i=front;i<size;i++){
data[i-front] = data[i];
}
//隊尾前移front位
rear-=front;
//隊頭指向0
front=0;
}
}
//隊尾插入元素
data[rear]=element;
rear++;
size++;
}
/**
* 出隊
* @return
*/
public Object deQueue() {
if (isEmpty()){
throw new RuntimeException("隊空");
}
//取隊頭元素
Object f=data[front];
//隊頭陣列元素指向null,幫助gc
data[front]=null;
//隊首指向下一元素
front++;
//元素個數減1
size--;
//返回隊首元素
return f;
}
/**
* 取隊首元素(不刪除隊首元素)
* @return
*/
public Object getHead() {
if (isEmpty()){
throw new RuntimeException("隊空");
}
return data[front];
}
}
時間複雜度分析:
- 入隊:平均O(1),最壞情況(擴容)O(n)
- 出隊:O(1)
- 取隊首:O(1)
3、鏈式佇列
這裡使用單向連結串列來實現鏈式佇列。
入隊是將隊尾指向插入的新元素,出隊是將隊頭指向隊頭的下一個元素。
/**
* @Author 三分惡
* @Date 2020/8/27
* @Description
*/
public class LinkedQueue implements Queue{
/**
* 節點類
* @param <T>
*/
class Node<T>{
private Object data; //資料
private Node next; //下一節點
Node(Object it, Node nextVal){
this.data=it;
this.next=nextVal;
}
}
private Node front; //隊頭
private Node rear; //隊尾
private int size; //佇列元素個數
/**
* 判空
* @return
*/
public boolean isEmpty() {
return size==0;
}
public int size() {
return size;
}
/**
* 入棧
* @param element
*/
public void enQueue(Object element) {
Node node=new Node(element,null);
//如果佇列為空
if (rear==null){
rear=node;
front=node;
}else{
rear.next=node;
}
size++;
}
/**
* 出隊
* @return
*/
public Object deQueue() {
//佇列為空
if (front==null){
throw new RuntimeException("佇列為空");
}
//隊頭
Node node=front;
front=front.next;
size--;
return node.data;
}
/**
* 取隊頭元素
*/
public Object getHead() {
//佇列為空
if (front==null){
throw new RuntimeException("佇列為空");
}
return front.data;
}
}
時間複雜度分析:
- 入隊:O(1)
- 出隊:O(1)
- 取隊首:O(1)
除此之外,順序佇列有變種迴圈佇列,當rear到達陣列的最大下標時,重新指回陣列下標為0的位置;
鏈式佇列有雙端佇列,隊頭、隊尾都可以進行入隊、出隊操作的佇列,可以通過雙向連結串列實現;
4、java中的佇列
java中有一個佇列介面java.util.Queue,定義了佇列的一些方法。
它有一個子介面,java.util.Deque,定義了雙端佇列的方法。
LinkedList實現了java.util.Deque介面,所以LinkedList能作為佇列也能作為雙端佇列使用。詳見LinkedList原始碼閱讀筆記。
原始碼地址:https://gitee.com/LaughterYoung/data-structure-learn.git
上一篇:重學資料結構(二、棧)
本文為學習筆記類部落格,主要資料來源如下!
參考:
【1】:鄧俊輝 編著. 《資料結構與演算法》
【2】:王世民 等編著 . 《資料結構與演算法分析》
【3】: Michael T. Goodrich 等編著.《Data-Structures-and-Algorithms-in-Java-6th-Edition》
【4】:嚴蔚敏、吳偉民 編著 . 《資料結構》
【5】:程傑 編著 . 《大話資料結構》
【6】:看完這篇你還不知道這些佇列,我這些圖白作了