队列的基本操作

定义和特点

和栈相反,队列(queue)是一种先进先出的线性表。它只允许在表的一端进行插入,而在另一端删除元素。允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)

假设队列为q=(a1,a2,…,an),那么,a1就是队头元素,an就是队尾元素。队列中的元素是按照a1,a2,…,an的顺序进入的,退出队列也只能按照这个次序依次退出。
在这里插入图片描述


队列的抽象数据类型定义:

ADT Queue{
数据对象:D={ai | ai∈ElemSet,i=1,2,…,n,n≥0}
数据关系:R={< a(i-1) , ai > | a(i-1), ai ∈ D,i=2,…,n}
基本操作
InitQueue(&Q) 构造一个空队列Q
DestroyQueue(&Q) 销毁队列Q
ClearQueue(&Q) 清空队列Q
QueueEmpty(Q) 若Q为空队列,返回true,否则返回false
QueueLength(Q) 返回Q的元素个数,即队列的长度
GetHead(Q) 返回Q的队头元素
EnQueue(&Q,e) 插入元素e为Q的新的队尾元素
DeQueue(&Q,&e) 删Queue除Q的队头元素,并用e返回其值
QueueTraverse(Q)从队头到队尾,依次对Q的每个元素访问
}ADT Queue


循环队列——队列的顺序表示和实现

队列的顺序存储结构:

#define MAXQSIZE 100     //队列可能达到的最大长度
typedef struct
{
    QElemType *base;     //存储空间的基地址
    int front;           //头指针
    int rear;            //尾指针
}SqQueue;   

为了在c语言中描述方便起见,在此约定:初始化创建空队列时,令front=rear=0,每当插入新的队列尾元素时,尾指针rear增1;每当删除队列头元素时,头指针front增1。因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置,如图:
在这里插入图片描述
(d)图状态时不可再继续插入新的队尾元素,否则会出现溢出现象,即因数组越界而导致程序的非法操作错误。事实上此时的队列实际可用空间并未占满,所以称为这种现象为“假溢出”现象。
怎样解决假溢出问题?一个巧妙的办法是将顺序队列变为一个环状的空间,称之为循环队列
在这里插入图片描述
头、尾指针以及队列元素之间的关系不变,只是在循环队列中,头、尾指针“依次增1”的操作可用“模”运算来实现。通过取模,头指针和尾指针就可以在顺序表空间内以头尾衔接的方式“循环”移动。
在图3.14(a)中,队头元素是J5,在元素J6入队之前,在Q.rear的值为5,当元素J6入队之后,通过“模”运算,Q.rear=(Q.rear+1)%6,得到Q.rear的值为0.而不会出现“假溢出”现象。
在这里插入图片描述
在图3.14(b)中,J7,J8,J9,J10相继入队,则队列空间均被栈满,此时头指针、尾指针相同。
在图3.14(c)中,若J5和J6相继从图3.14(a)所示的队列出队,使队列此时呈“空”的状态,头、尾指针也是相同的。
那要怎样区别队满还是队空呢?
处理方法:
少用一个元素空间,即队列空间大小为m时,有m-1个元素就认为是队满
这样判断队空的条件不变(即头尾指针的值相同),则为队空;而当尾指针在循环意义上加1后是等于头指针,则认为是队满。
队空的条件:Q.front == Q.rear
队满的条件:(Q.rear+1)%MAXQSIZE==Q.front
如图3.14(d)所示,当J7、J8、J9进入图3.14(a)所示的队列后,(Q.rear+1)%MAXQSIZE的值等于Q.front,此时认为队满。

//base[0]接在base[MAXQSIZE-1]之后,若rest+1==M,则令rear=0;
插入元素:Q.base[Q.rear]=x;
        Q.rear=(Q.rear+1)%MAXQSIZE;

删除元素:x=Q.base[s.front];
        Q.front=(Q.front+1)%MAXQSIZE;


①初始化

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

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

②求队列长度

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

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

③入队

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

Status EnQueue(SqQueue &Q,QElemType e)
{//插入e为Q的新的队尾元素
    if((Q.rear+1)%MAXQSIZE==Q.front)    //队满
       return ERROR;
    Q.base[Q.rear]=e;                   //新元素插入队尾
    Q.rear=(Q.rear+1)%MAXQSIZE;         //队尾指针加1
    return OK;
 }     

④出队

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

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;       //队头指针加
    return OK;
}    

⑤取队头元素

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

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

链队——队列的链式表示和实现

为了操作方便起见,给链队添加一个头结点,并令头指针始终指向头节点。
队列的链式存储结构:

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

在这里插入图片描述

①初始化

队列的初始化操作就是构造一个只有头节点的空队

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

②入队

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

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;
}     

③出队

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

Status DEQueue(LinkQueue &Q,QElemType &e)
{//删除Q的队头元素,用e返回其值
     if(Q.front==Q.rear)  return 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;
}     

④取队头元素

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

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

借鉴:《数据结构》 严蔚敏

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值