簡介

佇列 (Queue) 中文也翻作隊列,顧名思義是一種像排隊一樣的概念,以生活中的情況為例如下圖
佇列 (Queue)

人們一個接一個的從隊伍後面加入排隊,而窗口的服務人員則從最前面的民眾一個接一個處理事務;當然現實生活是會有中途離開的人,而在程式世界裡面一般情況是不會有。

在這個模式下我們可以知道他是一種先進先出 (First-In-First-Out, FIFO) 的排程,而在此資料結構中至少會實作兩個操作:

  1. enqueue:將資料放入佇列尾端。(註:C++ 中用 push、Java 用 offer、也有 add 等不同的用字)
  2. dequeue:取出佇列前端之資料。(註:C++ 中用 pop、Java 用 poll、也有 remove 等不同的用字)

有時候也會多實作一些額外的操作以方便使用,例如:

  1. peek:看佇列前端的資料而不取出。(註:也有 front 等不同的用字)
  2. size:取得佇列的數目。

在實作上一般使用連結串列 (LinkedList) 來實作,使用陣列同樣可以達成,但較為複雜:

連結串列

用指標將資料串起來,將新的東西不斷接在最後面,而取出時則移除最前面的東西即可。由於加入和移除的端點不同,如果只記住第一個節點的話,在加入資料時,必須每次都向後找到最後一個節點會耗費許多時間,為了讓加入和移除都在 O(1) 的時間完成,可以同時記錄最前面和最後面的節點。

陣列

佇列建立時即建立一個陣列,但由於加入和刪除的端點不同,所以我們需要一個索引來記錄開始位址和一個索引來記錄結束位址,如下圖所示:
佇列 (Queue)

然而我們會發現,不斷的 enqueue 和 dequeue 之後,佇列的範圍會慢慢往後移,直到陣列的盡頭,為了永續經營,我們必須重新使用前面的部分,所以我們還必須多進行一個轉換的動作。

實作上我只有額外多紀錄開始索引,而利用序列數目、陣列長度和簡單的取餘數方式來算出結束索引,公式如下:

1
下一個佇列索引位址 = (開始索引位址 + 序列數目) % 陣列長度

以上圖情況為例:

1
2
3
(0 + 3) % 6 = 3
(3 + 3) % 6 = 0
(4 + 3) % 6 = 1

另外,在實作佇列擴充,進行陣列的複製動作時,需將陣列元素重新整理,將開始位址的元素複製到新陣列的起始位置,否則資料會亂掉,如下圖所示:
佇列 (Queue)

語法

C#

抽象類別

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.Collections.Generic;
using System.Text;

namespace Queue
{
abstract class Queue<T>
{
public int Count { get; protected set; }
abstract public void Enqueue(T value);
abstract public T Dequeue();
abstract public T Peek();
}
}

連結串列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System;
using System.Collections.Generic;
using System.Text;

namespace Queue
{
class Node<T>
{
public Node<T> Next { get; internal set; }
public T Value { get; internal set; }

public Node(T value)
{
Value = value;
}
}

class LinkedListQueue<T> : Queue<T>
{
private Node<T> first;
private Node<T> last;

public override void Enqueue(T value)
{
Node<T> node = new Node<T>(value);
if (Count == 0)
first = node;
else
last.Next = node;
last = node;
++Count;
}

public override T Dequeue()
{
Node<T> node = first;
first = first.Next;
node.Next = null;
--Count;
return node.Value;
}

public override T Peek()
{
return first.Value;
}
}
}

陣列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Queue
{
class ArrayQueue<T> : Queue<T>
{
private int capacity;
private int first;
private T[] array;

public ArrayQueue()
: this(10240)
{
}

public ArrayQueue(int capacity)
{
this.capacity = capacity;
array = new T[capacity];
}

public override void Enqueue(T value)
{
if (Count == array.Length)
{
T[] newArray = new T[array.Length + capacity];
Array.Copy(array, first, newArray, 0, array.Length - first);
Array.Copy(array, 0, newArray, array.Length - first, first);
array = newArray;
first = 0;
}
array[(first + Count) % array.Length] = value;
++Count;
}

public override T Dequeue()
{
T value = array[first];
array[first] = default(T);
first = (first + 1) % array.Length;
--Count;
return value;
}

public override T Peek()
{
return array[first];
}
}
}

Java

抽象類別

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Queue<T> {
protected int count;

abstract public void offer(T value);
abstract public T poll();
abstract public T peek();

public int size()
{
return count;
}
}

連結串列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Node<T>
{
private Node<T> next;
private T value;

public Node<T> getNext()
{
return next;
}

void setNext(Node<T> next)
{
this.next = next;
}

public T getValue()
{
return value;
}

void setValue(T value)
{
this.value = value;
}

public Node(T value)
{
this.value = value;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class LinkedListQueue<T> extends Queue<T> {

private Node<T> first;
private Node<T> last;

@Override
public void offer(T value) {
// TODO Auto-generated method stub
Node<T> node = new Node<T>(value);
if (count == 0)
first = node;
else
last.setNext(node);
last = node;
++count;
}

@Override
public T poll() {
// TODO Auto-generated method stub
Node<T> node = first;
first = first.getNext();
node.setNext(null);
--count;
return node.getValue();
}

@Override
public T peek() {
// TODO Auto-generated method stub
return first.getValue();
}
}

陣列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class ArrayQueue<T> extends Queue<T> {

private int capacity;
private int first;
private T[] array;

public ArrayQueue()
{
this(10240);
}

@SuppressWarnings("unchecked")
public ArrayQueue(int capacity)
{
this.capacity = capacity;
array = (T[]) new Object[capacity];
}

@SuppressWarnings("unchecked")
@Override
public void offer(T value) {
// TODO Auto-generated method stub

if (count == array.length)
{
T[] newArray = (T[]) new Object[array.length + capacity];
System.arraycopy(array, first, newArray, 0, array.length - first);
System.arraycopy(array, 0, newArray, array.length - first, first);
array = newArray;
first = 0;
}
array[(first + count) % array.length] = value;
++count;
}

@Override
public T poll() {
// TODO Auto-generated method stub
T value = array[first];
array[first] = null;
first = (first + 1) % array.length;
--count;
return value;
}

@Override
public T peek() {
// TODO Auto-generated method stub
return array[first];
}
}

C++

抽象類別

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef QUEUE_H
#define QUEUE_H

template<typename T>
class Queue
{
public:
Queue()
{
count = 0;
}
int size()
{
return count;
}
virtual void push(T value) = 0;
virtual T pop() = 0;
virtual T front() = 0;
protected:
int count;
private:
};

#endif // QUEUE_H

連結串列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef LINKEDLISTQUEUE_H
#define LINKEDLISTQUEUE_H

#include "Queue.h"

template<typename T>
class Node
{
public:
Node(T v)
{
value = v;
next = 0;
}
~Node()
{
delete next;
}
Node<T>* next;
T value;
protected:
private:
};

template<typename T>
class LinkedListQueue : public Queue<T>
{
public:
~LinkedListQueue()
{
if(first != last)
delete first;
delete last;
}
void push(T value)
{
Node<T>* node = new Node<T>(value);
if (Queue<T>::count == 0)
first = node;
else
last->next = node;
last = node;
++Queue<T>::count;
}
T pop()
{
Node<T>* node = first;
first = first->next;
node->next = 0;
T value = node->value;
delete node;
--Queue<T>::count;
return value;
}

T front()
{
return first->value;
}
protected:
private:
Node<T>* first;
Node<T>* last;
};

#endif // LINKEDLISTQUEUE_H

陣列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#ifndef ARRAYQUEUE_H
#define ARRAYQUEUE_H

#include <memory.h>
#include "Queue.h"

template<typename T>
class ArrayQueue : public Queue<T>
{
public:
ArrayQueue(int capacity = 10240)
{
cap = capacity;
arrayLength = capacity;
array = new T[capacity];
}
~ArrayQueue()
{
delete [] array;
}
void push(T value)
{
if (Queue<T>::count == arrayLength)
{
T* newArray = new T[arrayLength + cap];
memcpy(newArray, array + first, (arrayLength - first) * sizeof(T));
memcpy(newArray + arrayLength - first, array, first * sizeof(T));
arrayLength += cap;
delete [] array;
array = newArray;
first = 0;
}
array[(first + Queue<T>::count) % arrayLength] = value;
++Queue<T>::count;
}

T pop()
{
T value = array[first];
array[first] = 0;
first = (first + 1) % arrayLength;
--Queue<T>::count;
return value;
}

T front()
{
return array[first];
}
protected:
private:
int arrayLength;
int cap;
int first;
T* array;
};

#endif // ARRAYQUEUE_H

enqueue和dequeue 500000次實測,同時候內建的類別比較

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
C#
Built-in Queue Enqueue Time: 30ms
Built-in Queue Dequeue Time: 10ms
LinkedListQueue Enqueue Time: 80ms
LinkedListQueue Pop Time: 20ms
ArrayQueue Enqueue Time: 60ms
ArrayQueue Dequeue Time: 10ms

Java
Built-in Queue Offer Time: 130ms
Built-in Queue Poll Time: 20ms
LinkedListQueue Offer Time: 178ms
LinkedListQueue Poll Time: 12ms
ArrayQueue Offer Time: 112ms
ArrayQueue Poll Time: 10ms

C++
Built-in Push Time: 10ms
Built-in Pop Time: 30ms
LinkedListQueue Push Time: 50ms
LinkedListQueue Pop Time: 40ms
ArrayQueue Push Time: 30ms
ArrayQueue Pop Time: 10ms

延伸閱讀

連結串列 (LinkedList)
堆疊 (Stack)