【数据结构与算法】第七章:队列的表示与操作实现

简介: 前面两章重点介绍了栈的表示与实现,本章将详细解释队列的表示与实现,以及相关的基本操作。

 

🙊🙊作者主页:🔗求不脱发的博客

📔📔 精选专栏:🔗数据结构与算法

📋📋 精彩摘要:前面两章重点介绍了栈的表示与实现,本章将详细解释队列的表示与实现,以及相关的基本操作。

💞💞觉得文章还不错的话欢迎大家点赞👍➕收藏⭐️➕评论💬支持博主🤞


📚目录

📖【数据结构与算法】第七章:队列的表示与操作实现

📝1️⃣队列的类型定义

📝2️⃣循环队列—队列的顺序表示和实现

📝3️⃣链队—队列的链式表示和实现


📖【数据结构与算法】第七章:队列的表示与操作实现


📝1️⃣队列的类型定义

队列的操作与栈的操作类似,不同的是,删除是在表的头部(即队头)进行。

✨队列的抽象数据类型

ADT Queue
{
    数据对象:D={ai|ai∈ElemSet,i=1,2,…,n,n≥0}
    数据关系:R={<ai−1,ai>|ai−1,ai∈D,i=2,…,n}
    基本操作:
        InitQueue(&Q)
        操作结果:构造一个空队列Q。
        DestroyQueue(&Q)
        初始条件:队列Q已存在。
        操作结果:队列Q被销毁,不再存在。
        ClearQueue(&Q)
        初始条件:队列Q已存在。
        操作结果:将Q清为空队列。
        QueueEmpty(Q)
        初始条件:队列Q已存在。
        操作结果:若Q为空队列,则返回true,否则返回false。
        QueueLength(Q)
        初始条件:队列Q已存在。
        操作结果:返回Q的元素个数,即队列的长度。
        GetHead(Q)
        初始条件:Q为非空队列。
        操作结果:返回Q的队头元素。
        EnQueue(&Q,e)
        初始条件:队列Q已存在。
        操作结果:插入元素e为Q的新的队尾元素。
        DeQueue(&Q,&e)
        初始条件:Q为非空队列。
        操作结果:删除Q的队头元素,并用e返回其值。
        QueueTraverse(Q)
        初始条件:Q已存在且非空。
        操作结果:从队头到队尾,依次对Q的每个数据元素访问。
        QueueTraverse(Q,visit())//遍历
}ADT Queue

image.gif

✨类型定义

#define M 100 //队列最大长度
Typedef struct
{
    Queue *base;//初始化的动态分配空间
    int front;//头指针
    int rear;//尾指针
}SqQueue;

image.gif


📝2️⃣循环队列—队列的顺序表示和实现

队列也有两种存储表示,顺序表示和链式表示。

和顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外,尚需附设两个整型变量front和rear分别指示队列头元素及队列尾元素的位置(后面分别称为头指针和尾指针)。

image.gif编辑

✨初始化

循环队列的初始化操作就是动态分配一个预定义大小为MAXQSIZE的数组空间。

【算法步骤】

    1. 为队列分配一个最大容量为MAXSIZE的数组空间,base指向数组空间的首地址。
    2. 头指针和尾指针置为零,表示队列为空。

    【算法描述】

    Status InitQueue(SqQueue &Q)
    {    //构造一个空队列Q
        Q.base=new QElemType[MAXQSIZE]; //为队列分配一个最大容量为MAXSIZE的数组空间
        if(!Q.base)
         exit(OVERFLOW); //存储分配失败
        Q.front=Q.rear=0; //头指针和尾指针置为零,队列为空
        return OK;
    }

    image.gif

    ✨求队列长度

    对于非循环队列,尾指针和头指针的差值便是队列长度,而对于循环队列,差值可能为负数,所以需要将差值加上MAXQSIZE,然后与MAXQSIZE求余。

    QueueLength = (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE

    【算法描述】

    int QueueLength(SqQueue Q)
    {    //返回Q的元素个数,即队列的长度
        return  (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
    }

    image.gif

    ✨入队

    入队操作是指在队尾插入一个新的元素。

    【算法步骤】

      1. 判断队列是否满,若满则返回ERROR。
      2. 将新元素插入队尾。
      3. 队尾指针加1。

      【算法描述】

      Status EnQueue(SqQueue &Q,QElemType e)
      {    //插入元素e为Q的新的队尾元素
          if((Q.rear+1)%MAXQSIZE==Q.front) //尾指针在循环意义上加1后等于头指针,表明队满
              return ERROR;
          Q.base[Q.rear]=e; //新元素插入队尾
          Q.rear=(Q.rear+1)%MAXQSIZE; //队尾指针加1
          return OK;
      }

      image.gif

      ✨出队

      出队操作是将队头元素删除。

      【算法步骤】

        1. 判断队列是否为空,若空则返回ERROR。
        2. 保存队头元素。
        3. 队头指针加1。

        【算法描述】

        Status DeQueue(SqQueue &Q,QElemType &e)
        {    //删除Q的队头元素,用e返回其值
            if(Q.front==Q.rear)
             return ERROR; //队空
            e=Q.base[Q.front]; //保存队头元素
            Q.front=(Q.front+1)%MAXQSIZE; //队头指针加1
            return OK;
        }

        image.gif

        ✨取队头元素

        当队列非空时,此操作返回当前队头元素的值,队头指针保持不变。

        【算法描述】

        SElemType GetHead(SqQueue Q)
        {    //返回Q的队头元素,不修改队头指针
            if(Q.front!=Q.rear) //队列非空
                return Q.base[Q.front]; //返回队头元素的值,队头指针不变
        }

        image.gif

        由上述分析可见,如果用户的应用程序中设有循环队列,则必须为它设定一个最大队列长度;若用户无法预估所用队列的最大长度,则宜采用链队。


        📝3️⃣链队—队列的链式表示和实现

        链队是指采用链式存储结构实现的队列。通常链队用单链表来表示。一个链队显然需要两个分别指示队头和队尾的指针(分别称为头指针和尾指针)才能唯一确定。这里和线性表的单链表一样,为了操作方便起见,给链队添加一个头结点,并令头指针始终指向头结点。

        image.gif编辑

        ✨链式存储结构

        typedef struct QNode
        {
            QElemType data;
            struct QNode next;
        }QNode, QueuePtr;
        typedef struct
        {
            QueuePtr front; //队头指针
            QueuePtr rear; //队尾指针
        }LinkQueue;

        image.gif

        链队的操作即为单链表插入和删除操作的特殊情况,只是需进一步修改尾指针或头指针。下面给出链队初始化、入队、出队操作的实现。

        ✨初始化

        链队的初始化操作就是构造一个只有一个头结点的空队。

        【算法步骤】

          1. 生成新结点作为头结点,队头和队尾指针指向此结点。
          2. 头结点的指针域置空。

          【算法描述】

          Status InitQueue(LinkQueue &Q)
          {    //构造一个空队列Q
              Q.front=Q.rear=new QNode; //生成新结点作为头结点,队头和队尾指针指向此结点
              Q.front->next=NULL; //头结点的指针域置空
              return OK;
          }

          image.gif

          ✨入队

          和循环队列的入队操作不同的是,链队在入队前不需要判断队是否满,需要为入队元素动态分配一个结点空间。

          【算法步骤】

            1. 为入队元素分配结点空间,用指针p指向。
            2. 将新结点数据域置为e。
            3. 将新结点插入到队尾。
            4. 修改队尾指针为p

            【算法描述】

            Status EnQueue(LinkQueue &Q,QElemType e)
            {    //插入元素e为Q的新的队尾元素
                p=new QNode; //为入队元素分配结点空间,用指针p指向
                p->data=e; //将新结点数据域置为e
                p->next=NULL; Q.rear->next=p; //将新结点插入到队尾
                Q.rear=p; //修改队尾指针
                return OK;
            }

            image.gif

            ✨出队

            和循环队列一样,链队在出队前也需要判断队列是否为空,不同的是,链队在出队后需要释放出队头元素的所占空间。

            【算法步骤】

              1. 判断队列是否为空,若空则返回ERROR。
              2. 临时保存队头元素的空间,以备释放。
              3. 修改队头指针,指向下一个结点。
              4. 判断出队元素是否为最后一个元素,若是,则将队尾指针重新赋值,指向头结点。
              5. 释放原队头元素的空间。

              【算法描述】

              Status DeQueue(LinkQueue &Q,QElemType &e)
              {    //删除Q的队头元素,用e返回其值
                  if(Q.front==Q.rear)
                   return ERROR; //若队列空,则返回ERROR
                  p=Q.front->next; //p指向队头元素
                  e=p->data; //e保存队头元素的值
                  Q.front->next=p->next; //修改头指针
                  if(Q.rear==p) Q.rear=Q.front; //最后一个元素被删,队尾指针指向头结点
                  delete p; //释放原队头元素的空间
                  return OK;
              }

              image.gif

              需要注意的是,在链队出队操作时还要考虑当队列中最后一个元素被删后,队列尾指针也丢失了,因此需对队尾指针重新赋值(指向头结点)。

              ✨取队列头元素

              与循环队列一样,当队列非空时,此操作返回当前队头元素的值,队头指针保持不变。

              【算法描述】

              SElemType GetHead(LinkQueue Q) 
              {    //返回Q的队头元素,不修改队头指针
                  if(Q.front!=Q.rear) //队列非空
                      return Q.front->next->data; //返回队头元素的值,队头指针不变
              }

              image.gif

              相关文章
              |
              18天前
              |
              C语言
              【数据结构】栈和队列(c语言实现)(附源码)
              本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
              95 9
              |
              1月前
              |
              缓存 算法 调度
              数据结构之 - 双端队列数据结构详解: 从基础到实现
              数据结构之 - 双端队列数据结构详解: 从基础到实现
              65 5
              |
              1月前
              |
              存储 算法 搜索推荐
              探索常见数据结构:数组、链表、栈、队列、树和图
              探索常见数据结构:数组、链表、栈、队列、树和图
              102 64
              |
              21天前
              |
              算法 安全 NoSQL
              2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
              数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
              |
              1月前
              初步认识栈和队列
              初步认识栈和队列
              60 10
              |
              1月前
              |
              存储 算法 定位技术
              数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
              这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
              21 0
              数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
              |
              1月前
              |
              存储 安全 Java
              【用Java学习数据结构系列】探索栈和队列的无尽秘密
              【用Java学习数据结构系列】探索栈和队列的无尽秘密
              30 2
              【数据结构】--- 栈和队列
              【数据结构】--- 栈和队列
              |
              1月前
              |
              消息中间件 存储 Java
              数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
              数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
              33 4
              |
              1月前
              【数据结构】-- 栈和队列
              【数据结构】-- 栈和队列
              17 0

              热门文章

              最新文章

              下一篇
              无影云桌面