数据结构笔记chapter03_2_队列

3.2.1 队列的基本概念

回顾:数据结构三要素:逻辑结构、存储结构、数据运算

线性表:具有相同数据类型的 n 个数据元素的有限序列,其中 n 为表长

栈(Stack):只允许在一端进行插入或者删除操作的线性表

队列(Queue):只允许在一端进行插入,另一端删除的线性表

特点:先入先出(FIFO)

重要术语:队头、队尾、空队列

  1. 队头:允许删除的一端
  2. 队尾:允许插入的一端

(尾插头删)

 


3.2.2 队列的顺序实现

基本操作:创、增、删、查(队头元素)、判空判满(增删查前必要判断)

1、创

静态数组存放队列元素

队头、队尾两个指针

  • 队头指针:指向队头元素
  • 队尾指针:指向队尾元素的后一个位置(下一个应该插入的位置)

//队列的顺序实现

#define MaxSize 10
typedef struct{
    
    ElemType data[MaxSize];  //静态数组存放队列元素
    int front,rear;    //队头和队尾指针
}SqQueue;

2、初始化

初始时队头、队尾指针指向0

//初始化队列

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

3、判空判满

  • 判空

bool QueueEmpty(SqQueue Q)

//判空操作

bool QueueEmpty(SqQueue Q)  //判断不变化,则不需要传址
{
    if(Q.rear==Q.front)   //队空条件
      return true;
    else
      return false;
}

  • 判满

循环队列:用模运算将存储空间在逻辑上变成了环状


Q.data[Q.rear]=x;   //新元素插入队尾

Q.rear=(Q.rear+1)%MaxSize;  //队尾指针+1取模

队列已满的条件:队尾的指针的再下一个位置是队头,即

(Q.rear+1)%MaxSize==Q.front

代价:牺牲一个存储的单元

4、入队操作

只能从队尾入队


//入队

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

5、出队操作与获取头元素

只能让队头元素出队

//出队(删除一个队头元素,并用x返回)

bool DeQueue(SqQueue &Q,ElemType &x){
    
    if(Q.rear==Q.front)
      return false;
    x=Q.data[Q.front];
    Q.front=(Q.front+1)%MaxSize;  //队头指针后移
    return true;
}

//获得队头元素的值,用x返回

bool GetHead(SqQueue Q,ElemType &x){
    
    if(Q.rear==Q.front)
      return false;
    x=Q.data[Q.front];
    return true;
}

队列元素个数:

\(rear+MaxSize-front)%MaxSize

判空判满小结:

1、有一个存储单元被牺牲

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

2、不浪费存储空间

  • 初始化时令size=0,记录现队列元素所用空间
  • 队空条件:size==0
  • 队满条件:size==MaxSize
  • 队列元素个数=size

 


3.2.3 队列的链式实现

链式存储实现队列:

  • 带头结点
  • 不带头结点

基本操作:创、增、删、查(队头元素)、判空(增删查前必要判断)

不存在判满

基本操作:

 1、创

//队列的链式实现

typedef struct LinkNode{   //链式队列结点
    
    ElemType data;
    struct LinkNode *next;
}LinkNode;

typedef struct{
    
    LinkNode *front,*rear;  //队列的队头和队尾指针
}LinkQueue;
  •  带头结点

初始时,front、rear都指向头结点

//初始化队列(带头结点)

void InitQueue(LinkQueue &Q){
    
    //初始时,front、rear都指向头结点
    Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));
    Q.front->next=NULL;
}


//判断队列是否为空
bool IsEmpty(LinkQueue Q){
    
    if(Q.front==Q.rear)
      return true;
    else
      return false;
    
}
  • 不带头结点

初始时front、rear都指向NULL

//初始化队列(不带头结点)
void InitQueue(LinkQueue &Q){
    
    //初始时front、rear都指向NULL
    Q.front=NULL;
    Q.rear=NULL;
}

//判断队列是否为空
bool IsEmpty(LinkQueue Q){
    
    if(Q.front==NULL)
      return true;
    else
      return false;
    
}

2、入队

void EnQueue(LinkQueue &Q,ElemType x)

  • 带头结点

       front不变

//新元素入队(带头结点)

void EnQueue(LinkQueue &Q,ElemType x){
    
    LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));  //开辟空间创建新节点,存放数据
    s->data=x;
    s->next=NULL;  //新结点next指向NIULL
    Q.rear->next=s;  //新结点插入rear之后
    Q.rear=s;  //修改尾指针
    
}
  • 不带头结点

      插入第一个元素时,修改队头队尾指针

      非第一个元素,将结点插入rear之后,修改尾指针

//新元素入队(不带头结点)

void EnQueue(LinkQueue &Q,ElemType x){
    
    LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode)); 
    s->data=x;
    s->next=NULL;
    if(Q.front==NULL)  //插入第一个元素,修改队头队尾指针
      {
          Q.front=s;
          Q.rear=s;
      }
    else{
        Q.rear->next=s;  //新结点插入rear之后
        Q.rear=s;  //修改尾指针
    }
}

3、出队

(1)带头结点

结点p指向队头要删除的元素,记得释放结点p

若是最后一个结点出队,则需要修改rear指针

//出队(带头结点)

bool DeQueue(LinkQueue &Q,ElemType &x){
    
    if(Q.front==Q.rear)
      return false;  //空队列
    LinkNode *p=Q.front->next;  //结点p指向队头要删除的元素
    x=p->data;
    Q.front->next=p->next;  //头结点指向下一个
    if(Q.rear==p)      //如果是删除最后一个元素,需要更改尾指针
      Q.rear=Q.front;
    free(p);  //释放p结点
    return true;
}

(2)不带头结点

若是最后一个结点出队,front、rear指针都需要修改

//出队(不带头结点)

bool DeQueue(LinkQueue &Q,ElemType &x){
    
    if(Q.front==NULL)  //判空
      return false;
    LinkNode *p=Q.front;  //结点p指向队头要删除的元素
    x=p->data;
    Q.front=p->next;  //头结点移动到下一个位置
    if(Q.rear==p)  //如果是删除最后一个元素
    {
        Q.front=NULL;
        Q.rear=NULL;
    }
    free(p);
    return true;
}

队列满:

(1)顺序存储:预分配空间耗尽时队满;

(2)链式存储:一般不会队满,除非内存不足。

小结:

1、增:注意第一个元素入队

2、删:注意最后一个元素出队


3.2.4 双端队列

栈:只允许从一端插入或者删除的线性表

队列:只允许从一端插入,另一端删除的线性表

双端队列:只允许从两端插入、两端删除的线性表(若只使用一端插入删除,则效果等同于栈)

 输入受限制的双端队列

输出受限制的双端队列

对输出序列合法性的判断:

1、栈:

卡特兰数 \frac{1}{n+1}C_{2n}^{n}

2、双端序列

栈中合法序列,双端序列中也一定合法

 

おすすめ

転載: blog.csdn.net/qq_41994825/article/details/121382132