数据结构复习指导之队列

文章目录

队列

考纲内容

复习提示

1.队列的基本概念

1.1队列的定义

1.2队列常见的基本操作

1.3重要考点

2.队列的顺序存储结构

2.1队列的顺序存储

2.2循环队列

2.3循环队列的操作

2.4重要考点

3.队列的链式存储结构

3.1队列的链式存储

3.2链式队列的基本操作

3.3重要考点

4.双端队列

4.1双端队列定义

4.2重要考点


队列

考纲内容

(一)栈和队列的基本概念

(二)栈和队列的顺序存储结构
(三)栈和队列的链式存储结构
(四)多维数组的存储

(五)特殊矩阵的压缩存储
(六)栈、队列和数组的应用

复习提示

本章通常以选择题的形式考查,题目不算难,但命题的形式比较灵活,其中栈(出入栈的过程、出栈序列的合法性)和队列的操作及其特征是重点。因为它们均是线性表的应用和推广,所以也容易出现在算法设计题中。此外,栈和队列的顺序存储、链式存储及其特点、双端队列的特点、栈和队列的常见应用,以及数组和特殊矩阵的压缩存储都是必须掌握的内容。

1.队列的基本概念

1.1队列的定义

队列(Queue)简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。这和我们日常生活中的排队是一致的,最早排队的也是最早离队的,其操作的特性是先进先出

队头(Front)。允许删除的一端,又称队首。
队尾(Rear)。允许插入的一端。
空队列。不含任何元素的空表。

1.2队列常见的基本操作

  • InitQueue(&Q):初始化队列,构造一个空队列 Q。
  • QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回 false。
  • EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾
  • DeQueue(&Q,&x):出队,若队列Q非空,删除队头元素,并用x返回。
  • GetHead(Q,&x):读队头元素,若队列Q非空,则将队头元素赋值给x。

需要注意的是,栈和队列是操作受限的线性表,因此不是任何对线性表的操作都可以作为栈和队列的操作。比如,不可以随便读取栈或队列中间的某个数据。

1.3重要考点

2.队列的顺序存储结构

2.1队列的顺序存储

队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针 front 指向队头元素,队尾指针rear 指向队尾元素的下一个位置(不同教材对front和rear的定义可能不同,例如,可以让 rear 指向队尾元素、front 指向队头元素。对于不同的定义,出队入队的操作是不同的)。

队列的顺序存储类型可描述为:

#define MaxSize 50             //定义队列中元素的最大个数
    typedef struct{
    ElemType data[MaxSize];    //用数组存放队列元素
    int front,rear;            //队头指针和队尾指针
} SqQueue;

初始时:Q.front=Q.rear=0
进队操作:队不满时,先送值到队尾元素,再将队尾指针加1。
出队操作:队不空时,先取队头元素值,再将队头指针加1。

图 3.6(a)所示为队列的初始状态,有 Q.front==Q.rear==0成立,该条件可以作为队列判空的条件。但能否用 Q.rear==Maxsize作为队列满的条件呢?显然不能,图 3.6(d)中,队列中仅有一个元素,但仍满足该条件。这时入队出现“上溢出”,但这种溢出并不是真正的溢出,在data 数组中依然存在可以存放元素的空位置,所以是一种“假溢出”。

2.2循环队列

上面指出了顺序队列“假溢出”的问题,这里引出循环队列的概念。将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。当队首指针 Q.front=MaxSize-1后,再前进一个位置就自动到0,这可以利用除法取余运算(%)来实现。

  • 初始时:Q.front=Q.rear=0。
  • 队首指针进1:Q.front=(Q.front+1)%Maxsize。
  • 队尾指针进1:Q.rear=(Q.rear+1)%Maxsize。
  • 队列长度:(Q.rear+MaxSize-Q.front)%MaxSize。

出队入队时:指针都按顺时针方向进1(如图3.7所示)。

那么,循环队列队空和队满的判断条件是什么呢?显然,队空的条件是Q.front==Q.rear。若入队元素的速度快于出队元素的速度,则队尾指针很快就会赶上队首指针,如图 3.7(d1)所示,此时可以看出队满时也有Q.front==Q.rear。循环队列出入队示意图如图 3.7所示。

为了区分是队空还是队满的情况,有三种处理方式:

1)  牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法,约定以“队头指针在队尾指针的下一位置作为队满的标志”,如图 3.7(d2)所示。

  • 队满条件:(Q.rear+1)%MaxSize==Q.front。
  • 队空条件:Q.front==Q.rear。
  • 队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize。

2)  类型中增设 size 数据成员,表示元素个数。删除成功 size 减 1,插入成功 size 加 1。队空时 Q.size==0:队满时Q.size==Maxsize,两种情况都有Q.front==Q.rear。

3)  类型中增设 tag 数据成员,以区分是队满还是队空。删除成功置 tag=0,若导致Q.front==Q.rear,则为队空;插入成功置 tag=1,若导致 Q.front==Q.rear,则为队满。

2.3循环队列的操作

(1)初始化

void InitQueue(SqQueue &Q){    //初始化队首、队尾指针
    Q.rear=Q.front=0;
}

(2)判队空

bool isEmpty(SqQueue Q){
    if(Q.rear==Q.front)    //队空条件
        return true;
else
        return false;
}

(3)入队

bool EnQueue(SqQueue &Q,ElemType x){
    if((Q.rear+1)号MaxSize==Q.front)        //队满则报错
        return false;
    Q.data[Q.rear]=x;
    Q.rear=(Q.rear+l)%MaxSize;              //队尾指针加1取模
    return true;
}

(4)出队

bool DeQueue(SqQueue &Q,ElemType &x){
    if(Q.rear==Q.front)                //队空则报错
        return false;
    x=Q.data[Q.front];
    Q.front=(Q.front+l)%MaxSize;      //队头指针加1取模
    return true;
}

2.4重要考点

3.队列的链式存储结构

3.1队列的链式存储

队列的链式表示称为链队列,它实际上是一个同时有队头指针和队尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点。

队列的链式存储类型可描述为:

typedef struct linkNode(      //链式队列结点
    ElemType data;
    struct linkNode *next;
}LinkNode;
typedef struct{               //链式队列
    LinkNode *front,*rear;    //队列的队头和队尾指针
}LinkQueue;

不带头结点时,当Q.front==NULL且Q.rear==NULL时,链式队列为空。

入队时,建立一个新结点,将新结点插入到链表的尾部,并让Q.rear 指向这个新插入的结点(若原队列为空队,则令Q.front也指向该结点)。出队时,首先判断队是否为空,若不空,则取出队头元素,将其从链表中摘除,并让Q.front 指向下一个结点(若该结点为最后一个结点,则置Q.front 和Q.rear 都为 NULL)。

不难看出,不带头结点的链式队列在操作上往往比较麻烦,因此通常将链式队列设计成一个带头结点的单链表,这样插入和删除操作就统一了,如图3.9所示。

用单链表表示的链式队列特别适合于数据元素变动比较大的情形,而且不存在队列满且产生溢出的问题。另外,假如程序中要使用多个队列,与多个栈的情形一样,最好使用链式队列,这样就不会出现存储分配不合理和“溢出”的问题。

3.2链式队列的基本操作

(1)初始化

void InitQueue(LinkQueue &Q){    //初始化带头结点的链队列
Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode)); //建立头结点
Q.front->next-NULL;              //初始为空
}

(2)判队空

bool IsEmpty(LinkQueue Q){
    if(Q.front==Q.rear)     //判空条件
        return true;
    else
        return false;
}

(3)入队

void EnQueue(LinkQueue &Q,ElemType &x){
LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode)); //创建新结点
s->data=x;
s->next=NULL;
Q.rear->next=s;    //插入链尾
Q.rear=s;          //修改尾指针

(4)出队

bool DeQueue(linkQueue &Q,ElemType &x){
    if(Q.front==Q.rear)
        return false;            //空队
    LinkNode *p=Q.front->next;
    x=p->data;
    Q.front->next=p->next;
    if(Q.rear==p)
        Q.rear=Q.front;          //若原队列中只有一个结点,删除后变空
    free(p);
    return true;
}

3.3重要考点

4.双端队列

4.1双端队列定义

双端队列是指允许两端都可以进行插入和删除操作的线性表,如图3.10所示。双端队列两端的地位是平等的,为了方便理解,将左端也视为前端,右端也视为后端。

在双端队列进队时,前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面。在双端队列出队时,无论是前端还是后端出队,先出的元素排列在后出的元素的前面

输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列称为输出受限的双端队列

输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列称为输入受限的双端队列。

若限定双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就蜕变为两个栈底相邻接的栈。

4.2重要考点

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心碎烤肠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值