1.現實世界裏咱們更多講究的是
先來後到
,先排隊先買票,這樣纔有秩序,畢竟咱們沒有計算機那麼有耐心
2.使用隊列結構能很好的模擬和解決相似生活中的事,好比消息的發送用隊列維護就是很是恰當的
3.隊列就像去動物園買票,先處理隊列的頭部,有新的人來了就後面排着去,慢慢等
4.還有一種頗有意思的隊列是循環隊列,它是因爲數組對頭部操做的困難性,從而轉變一種思路,讓數組也能很好的實現隊列結構,後面會仔細分析一下
5.本例操做演示源碼:但願你能夠和我在Github一同見證:DS4Android的誕生與成長,歡迎stargit
藍色區域是數組看見:初始化四個空間,不夠再擴容,空閒太多再縮容github
隊列是一種線性的數據結構
特性:尾部添加,頭部取出 即先進先出FIFO
操做:enqueue入隊 dequeue出隊 getFront查看隊首元素
複製代碼
兵馬未動,糧草先行
,有接口好辦事。編程
/**
* 做者:張風捷特烈
* 時間:2018/8/17 0017:15:57
* 郵箱:1981462002@qq.com
* 說明:隊列接口
*/
public interface IQueue<T> {
/**
* 入隊
* @param el 元素
*/
void enqueue(T el);
/**
* 出隊
* @return 元素
*/
T dequeue();
/**
* 獲取隊首元素
* @return 隊首元素
*/
T getFront();
/**
* 獲取隊列元素個數
* @return 元素個數
*/
int getSize();
/**
* 是否爲空
* @return 是否爲空
*/
boolean isEmpty();
}
複製代碼
普通隊列的數組實現----性能很是差,後面用數組實現循環隊列來優化
爲何會不好,由於尾添加和頭刪除,總有一個會讓全部的人挪一挪,後面會用數組實現循環隊列來優化數組
/**
* 做者:張風捷特烈
* 時間:2018/8/17 0017:15:57
* 郵箱:1981462002@qq.com
* 說明:普通隊列的數組實現----性能很是差,後面用數組實現循環隊列來優化
*/
public class ArrayChartQueue<E> implements IQueue<E> {
private ArrayChart<E> array;//成員變量
public ArrayChartQueue(int capacity) {
this.array = new ArrayChart<>(capacity);
}
public ArrayChartQueue() {
this.array = new ArrayChart<>();
}
@Override
public void enqueue(E el) {
array.add(el);
}
@Override
public E dequeue() {
return array.remove(0);
}
@Override
public E front() {
return array.get(0);
}
@Override
public int size() {
return array.size();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
}
複製代碼
因爲是基於數組來實現,因此一切的操做也是基於數組
初始四個大小的數組,就像招待處預留四把椅子,而後等椅子坐滿了,再來加椅子bash
數組表結構移除頭部...萬惡之源,千萬不要用,此處僅演示! 此處僅演示!此處僅演示!!
數組表結構移除頭部...萬惡之源,千萬不要用,此處僅演示! 此處僅演示!此處僅演示!!微信
基於數組實現的隊列在隊首取出時會使得整隊移動,效率會很低
可是壯哉我大數組
,豈會連個小小的隊列都搞不定,之後還哪還有臉立足王座...因而循環隊列出現了
提及循環你們腦子裏都是一個圈來回轉,循環小數表示不服... 只要有週期性就是循環,想成一個圈就狹隘了數據結構
不就是想要知道隊尾和隊首是那個嘛,我標出來,維護一下給你不就好了嗎
注意:這裏的優點在於維護了隊尾和隊首的標示,插入尾和刪除頭都是定點,並且數組總體不移動,而是標示在動
新加元素時,隊尾表識後移,不夠就擴容。刪除頭時隊首標示
循環隊列特色:
爲空時,`隊尾標示==隊首標示`,
隊列滿:`(隊尾標示+1)%數組長度==隊首標示`
循環隊列會使隊首前一個位置不可用。
複製代碼
/**
* 做者:張風捷特烈
* 時間:2018/8/17 0017:16:03
* 郵箱:1981462002@qq.com
* 說明:數組實現循環隊列
*/
public class ArrayLoopQueue<T> implements IQueue<T> {
private T[] data;// 隊列數據
private int head;//隊首標示
private int tail;//隊尾標示
private int size;//元素個數
public ArrayLoopQueue() {//無參構造:默認8個容量
this(8);
}
public ArrayLoopQueue(int capacity) {
// 由於會有一個浪費,因此+1
data = (T[]) new Object[capacity + 1];
head = 0;
tail = 0;
size = 0;
}
@Override
public void enqueue(T el) {
if (isFull()) {//加入時滿了---擴容
grow(capacity() * 2);
}
data[tail] = el;//在隊尾插入
//插入數據時對尾標示進行維護-----
tail = (tail + 1) % data.length;
size++;
}
@Override
public T dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("MakeSure it's not an empty } T ret = data[head]; data[head] = null;//讓隊首移除 //隊首移除時對首標示進行維護----- head = (head + 1) % data.length; size--; //閒置太多---縮容 if (size == capacity() / 4 && capacity() / 2 != 0 && size > 4) { grow(capacity() / 2); } return ret; } @Override public T front() { if (isEmpty()) { throw new IllegalArgumentException("MakeSure it's not an empty } return data[head]; } /** * 擴容/縮容 * * @param newCapacity 新的容量 */ private void grow(int newCapacity) { T[] newData = (T[]) new Object[newCapacity + 1]; for (int i = 0; i < size; i++) { // 此時在newData中隊首對齊回來,data中就得有一個front的偏移量 newData[i] = data[(i + head) % data.length]; } data = newData; head = 0; tail = size; } /** * 獲取容量 * * @return 容量 */ public int capacity() { return data.length - 1; } /** * 隊列元素個數 * * @return 元素個數 */ @Override public int size() { return size; } /** * 是否爲空 * * @return 是否爲空 */ @Override public boolean isEmpty() { return head == tail; } /** * 隊列是否滿了 * * @return 隊列是否滿了 */ private boolean isFull() { // tail的下一個位置等於head時 return (tail + 1) % data.length == head; } } 複製代碼
鏈表和隊列可謂自然配,鏈表的頭刪除,頭獲取很快,但尾添加要獲取尾部,須要遍歷一次,很差
但能夠維護首位標識,使隊尾也容易獲取。(固然你也能夠用雙鏈表...直接批件衣服,改都不用改)
註釋的很清楚了,看着代碼順一下,或debug走一波,我就不贅述了ide
/**
* 做者:張風捷特烈
* 時間:2018/8/17 0017:22:50
* 郵箱:1981462002@qq.com
* 說明:單鏈表實現隊列
*/
public class SingleLinkedQueue<T> implements IQueue<T> {
private Node head;//頭節點
private Node tail;//尾節點
private int size;//元素個數
public SingleLinkedQueue() {
head = null;
tail = null;
size = 0;
}
@Override
public void enqueue(T el) {//入隊
// 若是隊尾爲空,說明隊列是空的。由於tail一直指向最後一個非空節點。
if (tail == null) {
tail = new Node(null, el);//初始化
head = tail;
} else {
tail.next = new Node(null, el); // 新來的排到後面去
tail = tail.next; //更新隊尾
}
size++;
}
@Override
public T dequeue() {//出隊
if (isEmpty())
throw new IllegalArgumentException("MakeSure it's not an empty queue");
Node targetNode = head;//我是老大
head = head.next; // 我是老二,但我要篡位了...之後哥就是老大
targetNode.next = null; //前任老大走了....
if (head == null) {// 若是頭結點爲空
tail = null;
}
size--;
return targetNode.el;
}
@Override
public T front() {
if (isEmpty())
throw new IllegalArgumentException("MakeSure it's not an empty queue");
return head.el;
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
private class Node {
private T el;//改節點上的元素
private Node next; //下一節點
/**
* 兩參構造
*
* @param next //下一節點
* @param el 生成節點的元素值
*/
private Node(Node next, T el) {
this.el = el;
this.next = next;
}
}
}
複製代碼
方法\數量 | 1000 | 次10000次 | 10W次 | 100W次 | 1000次 |
---|---|---|---|---|---|
enqueue | 0.0006秒 | 0.0022秒 | 0.01571秒 | 0.06668秒 | 1.1375秒 |
dequeue | 0.0111秒 | 0.2707秒 | 18.7684秒 | ---- | -- |
方法\數量 | 1000 | 次10000次 | 10W次 | 100W次 | 1000次 |
---|---|---|---|---|---|
enqueue | 0.0004秒 | 0.0019秒 | 0.01775秒 | 0.05414秒 | 0.6896秒 |
dequeu | 0.0005秒 | 0.0021秒 | 0.0091秒 | 0.0360秒 | 0.3327秒 |
方法\數量 | 1000 | 次10000次 | 10W次 | 100W次 | 1000次 |
---|---|---|---|---|---|
enqueue | 0.0011秒 | 0.0031秒 | 0.0099秒 | 0.4881秒 | 3.1186秒 |
dequeue | 0.0002秒 | 0.0013秒 | 0.0046秒 | 0.0221秒 | 0.1388秒 |
可見循環隊列仍是蠻好的,壯哉,我大數組!
數組普通隊列,就認識一下吧...不要用它。
數組環形隊列和鏈表隊列的比較也就至關於數組和鏈表的比較oop
項目源碼 | 日期 | 備註 |
---|---|---|
V0.1--github | 2018-11-24 | 看得見的數據結構Android版之隊列結構的實現 |
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
個人github | 個人簡書 | 個人掘金 | 我的網站 |
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持post