队列的结构

队列是一种受限制的线性表数据结构,它允许在表的一端进行插入操作,在表的另一端进行删除操作。我们把允许插入的一端叫做队尾,允许删除的一端叫做队头
简单点说,队列是一种先进先出的线性表。

队列的顺序存储和链式存储

顺序存储:利用一组连续的存储单元存储从队头到队尾的元素,连续的存储单元通过数组实现,同时通过front表示队头在数组中下标,rear表示队尾在数组中的下标。
链式存储:利用单链表的头节点和为节点分别表示front和rear。只允许在头部删除,尾部插入。所以链式存储实际上就是受限制的单链表。
这一节我们介绍顺序存储的相关算法,链式存储的相关算法在下一章节会有详细的介绍。

队列的定义

我们在程序中声明一个Queue类来表示队列类,其中数组queues用于存放队列中的元素,size表示队列的容量,front表示队头在数组中索引,rear表示队尾在数组中的索引。在初始化的时候front和rear都设置为-1。
队列空的条件:rear==front
队列满的条件:rear=size-1

internal class Queue
{
    int[] queues;
    int size;
    int front;//队头
    int rear;//队尾
}

算法1:入队

入队就是在队尾插入一个元素,同时修改队尾指针rear,具体示例代码如下:

public void EnQueue(int data)
{
    if (rear == (size - 1))//队满 扩容
    {
        int[] newArr = new int[size * 2];
        size = size * 2;
        for (int i = 0; i < rear; i++)
        {
            newArr[i] = queues[i];
        }
        queues = newArr;
    }
    queues[++rear] = data;
}

算法2:出队

出队就是从队头删除一个元素,同时修改队头指针front。

public int Dequeue()
{
    if (front == rear)
    {
        throw new Exception("队列为空!");
    }
    return queues[++front];
}

算法3:队列是否为空

public bool IsEmpty()
{
    return front == rear;
}

算法4:查看队头元素

查看队头元素,不会执行出队操作,只是返回队头那个元素信息。

public int Peek()
{
    if (front == rear)
    {
        throw new Exception("队列为空!");
    }
    return queues[front + 1];
}

示例代码地址

https://gitee.com/jiamingyuanjin_admin/data_struct/blob/master/DataStruct/07Queue.cs

总结

数组作为队列容器的时候,有一个问题就是,当我们执行了一些次数的出队操作之后,数组中0到front位置的空间就会被浪费,有一个办法就是每次出队的时候,把数组中所有数据向前移动一位,队头始终指向数组的第一个位置,不会有空间浪费,但是这样显然太麻烦。还有一种办法就是等队尾指向数组最后一个位置的时候,将数组中的数据统一向前移动。如果不需要向前移动的情况下,就需要扩容。后面我们也会学习一种循环队列,可以帮助我们将数组空间有效利用起来。