队列是一种十分常见的数据结构,他的特点是先进先出。常见的一个使用地点便是对图的广度优先遍历中。这里要注意的是,优先队列和队列并不一样,实际上他们没有什么关系,关于优先队列可以查看我先前的文章()。
关于队列的先进先出的特点可以用下列数据举例:
有一组数据1,2,3,4,5。将这一组数据依次加入队列之中。那么将他们一次出队得到的结果就是1,2,3,4,5。这就是队列的特点,先进先出,越早进入的元素会越早出队。
和栈一样,队列也要两种实现方式,一种是使用链表实现,一种是使用数组实现。
优点是他不用考虑空间问题,只要内存足够便可以无限制的加入结点。
缺点则是因为链表每个元素都要记录下一个元素的引用,所以会有更大的空间消耗。
使用链表实现时,队列元素为空比较容易判断,只需要判断队头元素是不是空即可,而队列元素则需要一个变量去记录。
实现代码如下:
public class LinkedNode<T>
{
public T Value { get; set; }
public LinkedNode<T> Next { get; set; }
public LinkedNode() { }
public LinkedNode(T value) => Value = value;
}
public class LinkedQueue<T>
{
public LinkedNode<T> front;
public LinkedNode<T> rear;
private int count;
public int Count => count;
public LinkedQueue()
{
count = 0;
}
public void Enqueue(T value)
{
LinkedNode<T> node = new LinkedNode<T>(value);
if (front == null)
{
front = node;
rear = front;
}
else
{
rear.Next = node;
rear = node;
}
count++;
}
public T Dequeue()
{
if (front == null)
throw new Exception("Queue is empty");
T value = front.Value;
front = front.Next;
count--;
return value;
}
public T Peek()
{
if (front == null)
throw new Exception("Queue is empty");
return front.Value;
}
}
需要注意的是队列为空时需要做特殊处理。
数组实现的优点是对于每一个结点只需要存储他的值,比起链表实现大大减少了空间上的消耗。
缺点是数组是无法动态扩充容量的,要做的扩充只能重新开辟更大的数组将原数据在放入新数组实现扩容,在这个扩容过程中就需要遍历一次原数组,会有一定的时间损耗。
实现代码如下:
public class MyQueue<T>
{
private T[] items;
public int front;
public int rear;
private int count;
public int Count => count;
public int Capacity => items == null ? 0 : items.Length;
public MyQueue(int capacity = 10)
{
items = new T[capacity];
front = 0;
rear = 0;
count = 0;
}
public void Enqueue(T item)
{
if (count >= Capacity)
Expansion();
items[rear] = item;
rear = (rear + 1) % Capacity;
count++;
}
public T Dequeue()
{
if (count == 0)
throw new Exception("The queue is empty");
T value = items[front];
count--;
front = (front + 1) % Capacity;
return value;
}
public T Peek()
{
if (count == 0)
throw new Exception("Queue is empty");
return items[front];
}
private void Expansion()
{
int newCapacity = Capacity * 2;
T[] newItems = new T[newCapacity];
for (int i = front; i < count; i++)
newItems[i - front] = items[i % Capacity];
items = newItems;
front = 0;
rear = count;
}
}
这里也可以不使用count变量,只通过front和rear就可以完成判断。当front等于rear时,队列为空,当(rear + 1) % Capacity等于front时,队列为满。队列元素数量为(rear - front + Capacity) % Capacity。这里使用count可以更方便运算。
需要注意的是扩容时可以将原数据从新数组0下标处放入,然后更新队头指针为0,队尾指针为count。
这里测试选用leetcode题目,。
实现代码(官方队列)
public class Solution {
public int FindTheWinner(int n, int k) {
int result = 0;
for (int i = 1; i <= n; i++)
{
result = (result + k) % i;
}
return result + 1;
}
}
官方结果
链表队列
数组队列
测试均能通过
因篇幅问题不能全部显示,请点此查看更多更全内容