在日常生活中,队列的例子比比皆是,例如在车展排队买票,排在队头的处理完离开,后来的必须在队尾排队等候。在程序设计中,队列也有着广泛的应用,例如计算机的任务调度系统、为了削减高峰时期订单请求的消息队列等等。与栈类似,队列也是属于操作受限的线性表,不过队列是只允许在一端进行插入,在另一端进行删除。在其他数据结构如树的一些基本操作中(比如树的广度优先遍历)也需要借助队列来实现,因此这里我们来看看队列。
1.1 队列的基本特征
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。它是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
1.2 队列的基本操作
(1)入队(Enqueue):将一个数据元素插入队尾;
(2)出队(Dequeue):读取队头节点数据并删除该节点;
二、队列的基本实现
既然队列也属于特殊的线性表,那么其实现也会有两种形式:顺序存储结构和链式存储结构。首先,对于Queue,我们希望能够提供以下几个方法供调用:
|
Queue<T>() |
创建一个空的队列 |
|
void Enqueue(T s) |
往队列中添加一个新的元素 |
|
T Dequeue() |
移除队列中最早添加的元素 |
|
bool IsEmpty() |
队列是否为空 |
|
int Size() |
队列中元素的个数 |
2.1 队列的顺序存储实现
与Stack不同,在队列中我们需要定义一个head队头“指针”和tail队尾“指针”,当新元素入队时tail+1,当老元素出队时head+1。下面重点来看看Enqueue和Dequeue两个方法的代码实现。
(1)入队:Enqueue
public void EnQueue(T item) { if (Size == items.Length) { // 扩大数组容量 ResizeCapacity(items.Length * 2); } items[tail] = item; tail++; size++; }
新元素入队后,tail队尾指针向前移动指向下一个新元素要插入的位置;这里仍然模仿.NET中的实现,在数组容量不足时及时进行扩容以容纳新元素入队。
(2)出队:Dequeue
public T DeQueue() { if (Size == 0) { return default(T); } T item = items[head]; items[head] = default(T); head++; if (head > 0 && Size == items.Length / 4) { // 缩小数组容量 ResizeCapacity(items.Length / 2); } size--; return item; }
在对老元素进行出队操作时,首先取得head指针所指向的老元素,然后将head指针向前移动一位指向下一个将出队的老元素。这里将要出队的元素所在数组中的位置重置为默认值。最后判断容量是否过小,如果是则进行数组容量的缩小。
下面是完整的队列模拟实现代码,仅供参考,这里就不再做基本功能测试了,有兴趣的读者可以自行测试:
/// <summary> /// 基于数组的队列实现 /// </summary> /// <typeparam name="T">类型</typeparam> public class MyArrayQueue<T> { private T[] items; private int size; private int head; private int tail; public MyArrayQueue(int capacity) { this.items = new T[capacity]; this.size = 0; this.head = this.tail = 0; } /// <summary> /// 入队 /// </summary> /// <param name="item">入队元素</param> public void EnQueue(T item) { if (Size == items.Length) { // 扩大数组容量 ResizeCapacity(items.Length * 2); } items[tail] = item; tail++; size++; } /// <summary>v /// 出队 /// </summary> /// <returns>出队元素</returns> public T DeQueue() { if (Size == 0) { return default(T); } T item = items[head]; items[head] = default(T); head++; if (head > 0 && Size == items.Length / 4) { // 缩小数组容量 ResizeCapacity(items.Length / 2); } size--; return item; } /// <summary> /// 重置数组大小 /// </summary> /// <param name="newCapacity">新的容量</param> private void ResizeCapacity(int newCapacity) { T[] newItems = new T[newCapacity]; int index = 0; if (newCapacity > items.Length) { for (int i = 0; i < items.Length; i++) { newItems[index++] = items[i]; } } else { for (int i = 0; i < items.Length; i++) { if (!items[i].Equals(default(T))) { newItems[index++] = items[i]; } } head = tail = 0; } items = newItems; } /// <summary> /// 栈是否为空 /// </summary> /// <returns>true/false</returns> public bool IsEmpty() { return this.size == 0; } /// <summary> /// 栈中节点个数 /// </summary> public int Size { get { return this.size; } } }