我理解的數據結構(三)—— 隊列(Queue)
阿新 • • 發佈:2018-11-17
table can 需要 isempty sys 擴展 double start segment
我理解的數據結構(三)—— 隊列(Queue)
一、隊列
- 隊列是一種線性結構
- 相比數組,隊列對應的操作是數組的子集
- 只能從一端(隊尾)添加元素,只能從另一端(隊首)取出元素
- 隊列是一種先進先出的數據結構(FIFO)
二、數組隊列與循環隊列
1. 數組隊列
如果你有看過我之前的文章不要小看了數組或者棧,你就會發現,自己封裝一個數組隊列是如此的輕松加愉快!
(1)先定義一個接口,接口中定義隊列需要實現的方法
public interface Queue<E> { int getSize(); boolean isEmpty(); // 查看隊首元素 E getFront(); // 入隊 void enqueue(E ele); // 出隊 E dequeue(); }
(2)實現數組隊列
public class ArrayQueue<E> implements Queue<E> { // 這裏的數組是在之前的文章中封裝好的,直接拿來用就好了 private ArrayNew<E> array; public ArrayQueue(int capacity) { array = new ArrayNew<>(capacity); } public ArrayQueue() { this(10); } public int getCapacity() { return array.getCapacity(); } @Override public int getSize() { return array.getSize(); } @Override public boolean isEmpty() { return array.isEmpty(); } @Override public E getFront() { return array.getFirst(); } @Override public void enqueue(E ele) { array.addLast(ele); } @Override public E dequeue() { return array.removeFirst(); } @Override public String toString() { StringBuffer res = new StringBuffer(); res.append(String.format("arrayQueue: size = %d, capacity = %d\n", getSize(), getCapacity())); res.append("front ["); for (int i = 0; i < array.getSize(); i++) { res.append(array.get(i)); if (i != getSize() - 1) { res.append(", "); } } res.append("] tail"); return res.toString(); } }
(3)數組隊列的復雜度
方法 | 復雜度 |
---|---|
enqueue |
O(1) 均攤 |
dequeue |
O(n) |
front |
O(1) |
getSize |
O(1) |
isEmpty |
O(1) |
這個時候我們會發現,在進行出隊操作的時候,數組隊列的復雜度是0(n),如果我們頻繁的進行出隊操作,那麽其實數組隊列的效率是很低的,如何提升數組隊列的性能呢?這個時候我們就要用到循環隊列了。
2. 循環隊列隊列
循環隊列的原理:
-
dequeue
時,不要在去除隊首元素時,把整體向前移動 - 維護
front
、tail
和size
這三個屬性 -
enqueue
的時候tail++
-
dequeue
front++
(1)實現循環隊列
public class LoopQueue<E> implements Queue<E> {
private E[] array;
private int size;
private int front;
private int tail;
public LoopQueue(int capacity) {
// 我們需要浪費一個空間去判斷隊列是否已滿,所以需要把capacity + 1
array = (E[])new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
// 返回用戶傳遞的隊列大小
public int getCapacity() {
return array.length - 1;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("Queue is empty. Can't get front.");
}
return array[0];
}
@Override
public void enqueue(E ele) {
if (front == (tail + 1) % array.length) {
// 擴展隊列長度為原長度2倍
resize(getCapacity() * 2);
}
array[tail] = ele;
size++;
tail = (tail + 1) % array.length;
}
@Override
public E dequeue() {
if (isEmpty()) { // 隊列為空
throw new IllegalArgumentException("Queue is empty. Can't get dequeue.");
}
E ele = array[front];
size--;
array[front] = null;
front = (front + 1) % array.length;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
resize(getCapacity() / 2);
}
return ele;
}
private void resize(int newCapacity) {
E[] newArray = (E[]) new Object[newCapacity + 1];
for (int i = 0; i < size; i++) {
newArray[i] = array[(front + i) % array.length];
}
array = newArray;
front = 0;
tail = size;
}
@Override
public String toString() {
StringBuffer res = new StringBuffer();
res.append(String.format("queue: size = %d, capacity = %d\n", getSize(), getCapacity()));
res.append("front [");
// 循環條件,和循環增量都要註意下
for (int i = front; i != tail; i = (i + 1) % array.length) {
res.append(array[i]);
if ((i + 1) % array.length != tail) {
res.append(", ");
}
}
res.append("] tail");
return res.toString();
}
}
(2)循環隊列的復雜度
方法 | 復雜度 |
---|---|
enqueue |
O(1) 均攤 |
dequeue |
O(1) 均攤 |
front |
O(1) |
getSize |
O(1) |
isEmpty |
O(1) |
三、用時間說話
(1)用時方法
public static double test(Queue<Integer> q, int opCount) {
// 納秒
long startTime = System.nanoTime();
Random random = new Random();
for (int i = 0; i < opCount; i++) {
q.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i = 0; i < opCount; i++) {
q.dequeue();
}
// 納秒
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
(2)調用
// 十萬次入隊和十萬次出隊操作
int opCount = 100000;
ArrayQueue<Integer> aq = new ArrayQueue<>();
double time1 = test(aq, opCount);
System.out.println(time1);
LoopQueue<Integer> lq = new LoopQueue<>();
double time2 = test(lq, opCount);
System.out.println(time2);
(3)結果
- 14.635995113
- 0.054536447
這個就是算法和數據結構的力量!
原文地址:https://segmentfault.com/a/1190000016147024
我理解的數據結構(三)—— 隊列(Queue)