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

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

 

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

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

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

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


📚目录

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

📝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

              相关文章
              |
              3天前
              |
              存储 Java
              【数据结构】优先级队列(堆)从实现到应用详解
              本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
              16 5
              【数据结构】优先级队列(堆)从实现到应用详解
              |
              11天前
              |
              存储 C语言
              数据结构基础详解(C语言): 栈与队列的详解附完整代码
              栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
              |
              12天前
              |
              Java
              【数据结构】栈和队列的深度探索,从实现到应用详解
              本文介绍了栈和队列这两种数据结构。栈是一种后进先出(LIFO)的数据结构,元素只能从栈顶进行插入和删除。栈的基本操作包括压栈、出栈、获取栈顶元素、判断是否为空及获取栈的大小。栈可以通过数组或链表实现,并可用于将递归转化为循环。队列则是一种先进先出(FIFO)的数据结构,元素只能从队尾插入,从队首移除。队列的基本操作包括入队、出队、获取队首元素、判断是否为空及获取队列大小。队列可通过双向链表或数组实现。此外,双端队列(Deque)支持两端插入和删除元素,提供了更丰富的操作。
              14 0
              【数据结构】栈和队列的深度探索,从实现到应用详解
              |
              1月前
              |
              缓存 算法 Java
              刷算法,你应该知道的队列经典应用
              文章介绍了队列的基本特性和经典应用,包括如何用队列实现栈、使用优先级队列解决Top K问题,并通过LeetCode题目示例展示了队列在算法实现中的应用。
              刷算法,你应该知道的队列经典应用
              【数据结构】栈和队列
              【数据结构】栈和队列
              |
              1月前
              |
              存储
              【数据结构】栈和队列-->理解和实现(赋源码)
              【数据结构】栈和队列-->理解和实现(赋源码)
              25 5
              |
              28天前
              |
              机器学习/深度学习 消息中间件 缓存
              栈与队列的实现
              栈与队列的实现
              37 0
              |
              1月前
              |
              测试技术
              【初阶数据结构篇】队列的实现(赋源码)
              首先队列和栈一样,不能进行遍历和随机访问,必须将队头出数据才能访问下一个,这样遍历求个数是不规范的。
              |
              1月前
              |
              算法
              【数据结构与算法】优先级队列
              【数据结构与算法】优先级队列
              11 0
              |
              1月前
              |
              存储 算法
              【数据结构与算法】队列(顺序存储)
              【数据结构与算法】队列(顺序存储)
              12 0