从0开始学习数据结构-队列的学习③

190 阅读4分钟

这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战

C语言中不能用动态分配的一堆数组来实现循环队列。

  • 如果用户的应用程序中设有循环队列,则必须为它设定一个足够大的队列长度
  • 如果用户无法预先估计所用队列的最大长度,则循环队列宜采用链式存储结构

队列的链式存储结构

链接队列的构造

以下几种场景采用链式存储结构比采用顺序存储结构更合适:

  • 使用过程中数据元素变动较大
  • 频繁的进行插入和删除操作

队列的链式存储结构是用一个线性链表来表示一个队列,队列中每一个元素对于链表中的一个链结点。—— 链接队列。

  • 队头指针front:线性链表第1个链结点的指针
  • 队尾指针rear:链表最后的链结点
  • 测试链接队列为空的条件是front为NULL

注意:限定只能在链头进行删除操作,在链尾进行插入操作。在链接队列中插入一个新的元素就是在链表的表尾链结点后添加一个新链结点;删除一个元素就是删除链表的第1个链结点,即链接队列的操作就是线性链表的插入和删除操作的特殊情况,只需要修改头指针和尾指针。 链接队列的类型定义:

typedef struct node {
    QElemType data;
    struct node * link;
} QNode, *DLink; // 定义一个链表队列类型

链接队列的基本算法

  • ①初始化链接队列
void INITIALQLINK(QLink &front, QLink &rear) // ※
{
    return front = rear = NULL;
}
  • ② 判断链接队列是否为空 // ※
int EMPTYQLINK(QLink font)
{
    return front == NULL;
}
  • ③获取当前队头元素
int GETLINKQ(QLink front, QElemType &item)
{
    // front指向队头元素所在的链结点
    if (EMPTYQLINK(front)) {
        return 0; // 队列为空,操作失败,返回0
    }
    item = front->data; // 将队头元素的数据信息保存到变量item中
    return 1; // 队列非空,操作成功,返回1
    
}
  • ④ 链接队列的插入 在链表的末尾插入一个新结点
#define LEN sizeof(QNode)
int ADDLINKQ(QLink &front, QLink &rear, QElemType item) // ※
{
    // front和rear分别指向队头元素和队尾元素所在的链结点
    QLink p;
    if (!(p = (QLink)malloc(LEN))) // 申请一个链结点 ※
        return 0; // 插入失败,返回0
    p->data = item; // 将新结点的数据域data设置为item
    p->link = NULL; // 新结点的指针域设置为空
    if (front == NULL)  // ※ 将新结点插入空队
        front = p;  // ※
    else                    // 将item插入非空队的情况
        rear->link = p;  // ※ 将新结点插入尾节点的后面
    rear = p;  // ※ 修改队尾指针rear的指向
    return 1; // 插入成功,返回1
}

链接队列左插入操作一般不好产生溢出,除非整个可用空间全部被利用。

  • ⑤ 链接队列的删除 删除链表的第一个结点
int DELLINKQ(QLink &front, QElemType &item)
{
    // front 指向队头元素所在的链结点
    QLink p;
    if (EMPTYQLINK(front)) // ※
        return 0; // 队列为空,删除失败,返回0
    p = front; // ※
    item = p->data; // 保存要删除的结点的数据信息
    front = p->link; // front指向下一个结点  ※
    free(p); // 释放被删除头结点空间
    return 1; // 队列非空,删除成功,返回1
}

一般情况下,删除队头元素时只需要修改头结点中的指针,但是,当队列中最后一个元素被删除后,队列为空,此时队尾指针也丢失了,因此,还需要对队尾指针重新赋值。

  • ⑥ 链接队列的销毁 将队列对应链表中的所有链结点(的空间)释放,使之成为一个空队。 该过程与删除并释放一个线性链表的所有链结点(销毁一个线性链表)一样,只是在删除过程中没有另外定义临时变量,而是使用了队尾指针rear充当该角色。
void DESLINKQ(QLink &front, QLink &rear)
{
    while(front) {
        rear = front->link;
        free(front);
        front = rear;
    }
}

前5个算法的时间复杂度均为O(1),算法⑥的时间复杂度为O(n),其中n为当前队列的长度。