本文根據《大話數據結構》一書,實現了Java版的循環隊列、鏈隊列。java
隊列:只容許在一端進行插入操做,而在另外一端進行刪除操做的線性表。node
1.循環隊列數組
隊列的順序儲存結構:用數組存儲隊列,引入front指針指向隊頭元素,rear指針指向隊尾元素的下一個位置,當front=rear時,爲空隊列,結構以下圖所示。數據結構
當執行入隊操做時,若數組尾部已滿,而數組前部因有元素出隊而有空位時,咱們把新插入的元素從頭開始入隊,這樣就相似於頭尾相接的結構。ide
隊列的這種頭尾相接的順序存儲結構稱爲循環隊列,以下圖所示。測試
上面隊列的定義中提到,當front=rear時,爲空隊列,而在循環隊列中,隊列滿時,front也等於rear,將沒法判斷隊滿和空的狀況。this
一種辦法是設置一個標誌變量flag,當front=rear時,經過判斷flag是0仍是1來肯定隊列滿空狀況;spa
另外一種方法是,在數組中只剩一個空閒單位時,定義爲隊列滿,以下圖所示。(本文程序採用這種辦法)指針
由於rear可能比front大,也可能比front小,因此隊列滿的條件應該爲:(rear+1)%maxSize==front;同理,隊列長度的計算公式爲:(rear-front+maxSize)%maxSize。code
實現程序:
/** * <循環隊列> * * 注意點:表長的表示、隊列滿的判斷、front和rear的改變 * * @author Lai * */ public class SqQueue<E> { private E[] data; private int front; private int rear; private int maxSize; private static final int DEFAULT_SIZE= 10; /* * 初始化 */ public SqQueue(){ this(DEFAULT_SIZE); } public SqQueue(int maxSize){ data=(E[]) new Object[maxSize]; this.maxSize=maxSize; front=0; rear=0; } /* * 求循環隊列長度 */ public int getLength() { return (rear-front+maxSize)%maxSize; } /* * 入隊操做 */ public void enQueue(E e) { if((rear+1)%maxSize==front) throw new RuntimeException("隊列已滿,沒法入隊!"); data[rear]=e; rear=(rear+1)%maxSize; //不是rear=rear+1,當rear在數組尾部時,後移一位會轉到數組頭部 } /* * 出隊操做 */ public E deQueue() { if(rear==front) throw new RuntimeException("隊列爲空!"); E e=data[front]; front=(front+1)%maxSize; //不是front++,理由同rear return e; } /* * 打印操做 */ public void printQueue() { int k=front; for(int i=0;i<getLength();i++) { System.out.print(data[k]+" "); k=(k+1)%maxSize; } System.out.println(); } /* * 測試代碼 */ public static void main(String[] args) { SqQueue<String> aQueue=new SqQueue<>(5); aQueue.enQueue("a"); aQueue.enQueue("b"); aQueue.enQueue("c"); aQueue.enQueue("d"); aQueue.printQueue(); System.out.println("-----"); aQueue.getLength(); aQueue.deQueue(); aQueue.deQueue(); aQueue.enQueue("e"); aQueue.printQueue(); } }
a b c d ----- c d e
2.隊列的鏈式存儲結構
用單鏈表存儲隊列,稱爲鏈隊列。
定義front指針指向頭結點,rear指針指向終端結點,空隊列時,front和rear都指向頭結點。
實現程序:
/** * 鏈隊列 * * 注意點:出隊操做時,若隊頭是隊尾(即隊中僅有一個結點),則刪除後要將rear指向頭結點。 * * @author Yongh * * @param <E> */ public class LinkQueue<E> { private QNode front,rear; private int count; class QNode{ E data; QNode next; public QNode(E data,QNode next) { this.data=data; this.next=next; } } public LinkQueue() { front=new QNode(null, null); rear=front; count=0; } /* * 入隊操做 */ public void enQueue(E e) { QNode node=new QNode(e, null); rear.next=node; rear=node; count++; } /* * 出隊操做 */ public E deQueue() { if(rear==front) throw new RuntimeException("隊列爲空!"); QNode node=front.next; E e=node.data; front.next=node.next; //若隊頭是隊尾,則刪除後要將rear指向頭結點。 if(rear==node) rear=front; node=null; count--; //經過count來判斷,可能更容易理解 //if(count==0) // rear=front; return e; } /* * 獲取隊列長度 */ public int getLength() { return count; } /* * 打印輸出隊列 */ public void printQueue() { if(count==0) { System.out.println("空隊列"); }else { QNode node=front; for(int i=0;i<count;i++) { node=node.next; System.out.print(node.data+" "); } System.out.println(); } } /* * 測試代碼 */ public static void main(String[] args) { LinkQueue<String> lQueue =new LinkQueue<>(); lQueue.printQueue(); lQueue.enQueue("A"); lQueue.enQueue("B"); lQueue.enQueue("c"); lQueue.enQueue("D"); lQueue.printQueue();
lQueue.deQueue(); lQueue.deQueue(); lQueue.enQueue("E"); lQueue.printQueue(); } }
空隊列
A B c D
c D E
3.循環隊列和鏈隊列的選擇
基本操做時間都爲O(1)。但鏈隊列每次申請和釋放結點會存在一點時間開銷,且其須要存儲一個指針域;而循環隊列必須固定空間長度,存在空間浪費問題,且沒鏈隊列靈活。
綜上,在能夠肯定隊列長度最大值的狀況下,建議用循環隊列;當沒法預估隊列的長度時,使用鏈隊列。