11.队列的表示与实现

目录

一. 基本术语

二. 案例引入

三. 队列的表示与实现

(1)队列的顺序表示,循环顺序队列

1)循环队列的类型定义

2)循环队列的初始化

3)求循环队列的长度

4)循环队列入队

5)循环队列出队

 6)取队头元素

(2)队列的链式表示与实现

1)链队列的初始化(a)

2)链队列的销毁

3)链队列入队(c)

4)链队列出队(d)

5)查看队头元素


一. 基本术语

1.定义:只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表(头删尾插)。
2.逻辑结构:与线性表相同,仍为一对一关系。
3.存储结构:顺序队或链队,以循环顺序队列更常见。
4.运算规则:只能在队首Front和队尾Rear运算,且访问结点时依照先进先出(FIFO)的原则。
5.实现方式:关键是掌握插入(入队)和删除(出队)操作,具体实现依顺序队或链队的不同而不同。

二. 案例引入

假设在舞会上,男士和女士各自排成一队。舞会开始时依次从男队和女队的队头各出一人配成舞伴。如果两队初始人数不相同,则较长的那一队中未配对者等待下轮舞曲。现要求写一算法模拟上述舞伴配对问题。
【算法步骤】显然,先入队的男士或女士先出队配成舞伴。因此该问题具有典型的先进先出特性,可以用队列作为算法的数据结构。
(1)首先构造两个队列QM,QF;
(2)依次将队头元素出队配成舞伴;
(3)某队为空,则另外一队等待者则是下一舞曲第一个可获得舞伴的人。

三. 队列的表示与实现

描述队列的抽象数据类型(ADT):

ADT Queue{
    数据对象:D={ ai | ai ∈ElemSet, i=1,2..,n, n≥0 }
    数据关系:R1= { <ai-1, ai >| ai-1, ai∈D, i=2,...,n }
        //约定an端为队尾,a1端为队头。
    基本操作:
        InitQueue(&Q)  //操作结果:构造空队列Q
        DestroyQueue(&Q)  //条件:队列Q已存在;操作结果:队列Q被销毁
        ClearQueue(&Q)  //条件:队列Q已存在;操作结果:将Q清空
        QueueLength(Q)  //条件:队列Q已存在;操作结果:返回Q的元素个数,即队长
        GetHead(Q,&e)  //条件:Q为非空队列;操作结果:用e返回Q的队头元素
        EnQueue(&Q, e)  //条件:队列Q已存在操作结果:插入元素e为Q的队尾元素
        DeQueue(&Q,&e)  //条件:Q为非空队列;操作结果:删除Q的队头元素,用e返回值
        .....还有将队列置空、遍历队列等操作.....
}ADT Queue

队列也有两种存储方式:顺序存储与链式存储

(1)队列的顺序表示,循环顺序队列

队列的顺序表示-用一维数组base[MAXQSIZE],结构体定义如下:

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

和栈一样,这里我们仍然认为尾指针指向队尾元素的下一个位置。

初始,空队列下:front=rear=0;

入队:base[rear]=x;rear++;

出队:x=base[front];front++;空队标志:front==rear;

真溢出(front=0,rear=MAXQSIZE,下左)与假溢出(front>0,rear=MAXQSIZE,下右):

为了避免假溢出,引入循环队列,将队空间设想成一个循环的表,即分配给队列的m个存储单元可以循环使用,当rear为MAXQSIZE时,若向量的开始端空着,又可从头使用空着的空间。当front为MAXQSIZE时,
具体的说,把base[0]接在base[MAXQSIZE-1]之后,若rear+1==M,则令rear=0;实现方法:利用模(mod,C语言中:%)运算。
因此,插入元素和删除元素的代码可以写成以下形式:

//插入队列元素
Q.base[Q.rear]=x;
Q.rear=(Q.rear+1)% MAXQSIZE;
//删除队列元素
x=Q.base[s.front];
Q.front=(Q.front+1)% MAXQSIZE;

但是此时队空和队满都有front==rear:

解决方案:

  • 另外设一个标志以区别队空、队满
  • 另设一个变量,记录元素个数
  • 少用一个元素空间
     

我们介绍一下第三种方法:少用一个元素空间,当队列为空时仍然有front==rear;当队列为满时,队尾指针再+1,就能和队头指针重合。

1)循环队列的类型定义
# define MAXQSIZE 100
typedef struct{
    QElemType *base;  //动态分配存储空间
    int front;  //头指针,若队列不空指向第一个元素
    int rear;  //尾指针,若队列不空指向队列尾元素的下一个位置
}SqQueue;
2)循环队列的初始化
Status InitQueue(SqQueue &Q){  //构造一个空队列
    Q.base=new QElemType[MAXSIZE];  
    //或Q.base=(QElemType*)malloc(MAXSIZE*sizeof(QElemType));
    if(!Q.base) exit(OVERFLOW);  //存储分配失败
    Q.front=Q.rear=0;  //队头指针等于队尾指针并且置零
    return OK;
}
3)求循环队列的长度

int QueueLength(SqQueue Q){
    return((Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}
4)循环队列入队
Status EnQueue(SqQueue &Q,QElemType e){
    if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;  //队满
    Q.base[Q.rear]=e;  //插入队列
    Q.rear=(Q.rear+1)%MAXQSIZE; //队尾指针+1
    return OK;
}
5)循环队列出队
Status DeQueue(SqQueue &Q,QElemType e){
    if(Q.rear==Q.front) return ERROR;  //队空
    e=Q.base[Q.front];  //删除队头元素
    Q.front=(Q.front+1)%MAXQSIZE; //队头指针+1
    return OK;
}
 6)取队头元素
SElemType GetHead(SqQueue Q){
    if(Q.front!=Q.rear)  //队列不为空
        return Q.base[Q.front];  //返回队头元素的值,队头指针不变
}
(2)队列的链式表示与实现

链队列的结构体定义同链表,不同的地方是链式队列有头尾指针。因此,首先定义一个结构体QNode,表示队列中的节点。每个节点包含一个数据域data,用于存储节点的数据,以及一个指针域next,用于指向下一个节点。然后,定义了一个结构体LinkQueue,表示链式队列。每个链式队列包含一个队头指针front,用于指向队列的第一个节点,以及一个队尾指针rear,用于指向队列的最后一个节点。

typedef struct QNode 
{
    QElemType data;  //结点的数据域
    struct QNode *next;  //结点的指针域
}QNode,*QueuePtr;  

typedef struct{
    QueuePtr front;  //队头指针
    QueuePtr rear;  //队尾指针
}LinkQueue;

1)链队列的初始化(a)
Status InitQueue(LinkQueue &Q){
    Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));  //分配一个结点让头尾指针都指向它
    if(!Q.front) exit(OVERFLOW);
    Q.front->next=NULL;  //结点next域置空
    return OK;
}
2)链队列的销毁
Status DestroyQueue(LinkQueue &Q){
    while(Q.front){
        p=Q.front->next;  //先标记下一个结点,这里p也可以直接用Q.rear
        free(Q.front);
        Q.front=p;  //front指针指向下一个结点
    }
    return OK;
}
3)链队列入队(c)
Status EnQueue(LinkQueue &Q,QElemType e){
    p=(QueuePtr)malloc(sizeof(QNode));  //建立新结点
    if(!p) exit(OVERFLOW);
    p->data=e;
    p->next=NULL;
    Q.rear->next=p;  //队尾连接新结点
    Q.rear=p;
    return OK;
}
4)链队列出队(d)
Status DeQueue (LinkQueue &Q,QElemType &e){
    if(Q.front==Qrear) return ERROR;  //空队,报错
    p=Q.front->next;  //p指向被删除的结点
    e=p->data;
    Q.front->next=p->next;  //修改头指针next域指向下一个队头元素
    if(Q.rear==p) Q.rear=Q.front; //如果整个队列只有一个元素,这个元素被删掉以后还要修改尾指针
    delete p;
    return OK;
}
5)查看队头元素
Status GetHead(LinkQueue Q,QElemType &e){
    if(Q.front==Q.rear) return ERROR;
    e=Q.front->next->data;
    return OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北京地铁1号线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值