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

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

 

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

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

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

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


📚目录

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

📝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

              相关文章
              |
              2月前
              |
              C语言
              【数据结构】栈和队列(c语言实现)(附源码)
              本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
              284 9
              |
              11天前
              |
              存储 C语言 C++
              【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
              本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
              128 75
              |
              12天前
              |
              存储 C++ 索引
              【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
              【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
              34 13
              【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
              |
              12天前
              |
              存储 C语言 C++
              【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
              本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
              35 9
              |
              12天前
              |
              C++
              【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
              【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
              29 7
              |
              2月前
              |
              存储 缓存 算法
              在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
              在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
              87 5
              |
              2月前
              |
              算法 安全 NoSQL
              2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
              数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
              |
              3月前
              初步认识栈和队列
              初步认识栈和队列
              68 10
              |
              3月前
              |
              存储 算法 定位技术
              数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
              这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
              39 0
              数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
              |
              3月前
              |
              存储 安全 Java
              【用Java学习数据结构系列】探索栈和队列的无尽秘密
              【用Java学习数据结构系列】探索栈和队列的无尽秘密
              42 2

              热门文章

              最新文章