关于队列你需要知道的那点事

1.队列的基本概念

对于队列来说,元素是先进先出,遵循FIFO(First In First Out)原则

  • 队头(front):允许删除的一端,也成队首。
  • 队尾(rear):允许插入的一端。
  • 空队列: 不含任何元素的空表。

2.顺序队列

顺序队列的定义如下:

#include<stdio.h>
//顺序队列的定义
#define MAX_QUEUE_SIZE 100
typedef int ElementType;
typedef struct queue{
    
    
    ElementType Queue_arr[MAX_QUEUE_SIZE];
    int front;  //定义队首
    int rear;   //定义队尾 
}SqQueue;

初始化: SqQueue.front==SqQueue.rear == 0
进队:队不满时,将元素放入队尾,rear++
出队:队不空时,将元素放入队首,front++

3.循环队列

3.1 循坏队列的引入

为解决顺序队列的假溢出问题,引入循环队列。循环队列也是一种顺序存储结构

3.2 如何判断队满?

循环队列会出现无法区分队满的情况,为判断是否队满,主要引入以下三种方案:
1.增加lengh,入队与出队时,增加或减少length,当length==max_queue_size时,队满,要求length互斥。
2.增加flag,原理同length,也是要互斥。
3.循环意义下,尾指针加1是否等于头指针,若相等则认为队满。(假设最大能容纳元素为n,则(rear+1)mod(n)==font时,队满)

3.3 核心要点

核心点:

  • FIFO,先进先出
  • 入,尾动首不动
  • 出,首动尾不动

3.4 注意事项

常用的初始化方法:将front始终指向队首,rear指向队尾的下一个空位置。
当使用该初始化方法时,注意:
1.循环队列假设空间大小为n,则最多存储n-1个元素,会牺牲一个空间。

  • 队空条件:front==rear;
  • 队满条件: (rear + 1) % n == front;
  • 入队操作: rear = (rear + 1) % n;
  • 出队操作: front = (front + 1) % n;
  • 队伍中元素个数: length = (rear - front + n) % n

3.5 循环队列的初始化

循环队列的定义同顺序队列,初始化如下

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
//顺序队列的定义
#define MAX_QUEUE_SIZE 100
typedef int ElementType;
typedef struct queue{
    
    
    ElementType Queue_arr[MAX_QUEUE_SIZE];
    int front;  //定义队首
    int rear;   //定义队尾 
}SqQueue;

//循环队列的初始化(为解决假溢出问题)
SqQueue Init_CirQueue(){
    
    
    SqQueue Q;
    Q.front = Q.rear = 0;
    return Q;
}

3.6 入队操作

//循环队列的入队
bool Insert_CirQueue(SqQueue * Q, ElementType e){
    
    
    //循环队列的入队操作:将元素e插入到循环队列的末尾。
    //入队前,先判断队满
    if ((Q->rear + 1) % MAX_QUEUE_SIZE == Q->front) return false;
    // 元素入队
    Q->Queue_arr[Q->rear] = e;
    // 队尾指针向前移动
    Q->rear = (Q->rear + 1) % MAX_QUEUE_SIZE;
    return true;
}

3.7 出队操作

//循环队列的出队
bool Delete_CirQueue(SqQueue *Q, ElementType *x){
    
    
    //循环对列的出队操作:出队,并将出队的值赋给x
    //出队之前先判空
    if(Q->front == Q->rear) return false;
    //将队首元素的值赋给x
    *x = Q->Queue_arr[Q->front];
    //队首元素向后移动
    Q->front = (Q->front + 1) % MAX_QUEUE_SIZE;
    return true;
}

4.链队列

为解决队满的问题,引入链式存储结构,即链队列。
为实现操作,定义两个不同的数据结点:数据元素结点;包含队列的队首指针和队尾指针的结点。
若使用单链表来表示队列,则应该选用以下两种方式:
给定带尾指针的循环单链表;
给定头指针或为真的循环双链表。

4.1 链队列的定义

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
//链队列的定义
typedef int ElementType;
//定义数据元素结点
typedef struct Qnode{
    
    
    ElementType data;
    struct Qnode * next;
}QNode;
//定义队首、队尾指针数据结点。
typedef struct link_queue{
    
    
    QNode * front;
    QNode * rear;
}Link_Queue;

4.2 链队列的初始化

Link_Queue * init_link_queue(){
    
    
    // 链队列初始化
    QNode * p;
    Link_Queue * Q;
    p = (QNode *)malloc(sizeof(QNode));
    p->next = NULL;
    // 申请空间
    Q = (Link_Queue *)malloc(sizeof(Link_Queue));
    Q->front = Q->rear = p;
    return Q;
}

4.3 链队列的入队

bool Insert_Link_queue(Link_Queue * Q, ElementType e){
    
    
    QNode * p;
    //在队尾插入元素
    p = (QNode *)malloc(sizeof(QNode));
    // 若申请失败,则报错
    if (!p) return false;
    // 尾插法
    p->data = e;
    p->next = NULL;
    Q->rear->next = p;
    Q->rear = p;
    //printf("放入的元素值为:%d\n", e);
    return true;
}


4.4 链队列的出队

bool Delete_Link_queue(Link_Queue * Q, ElementType * e){
    
    
    // 出队列,并将该元素的值传给e
    QNode * p;
    if (Q->front == Q->rear) return false;
    p = Q->front->next;
    //printf("取出元素%d\n", p->data);
    * e = p->data;
    Q->front->next = p->next;
    //删除最后一个元素时,首尾都要改,改为初始状态。
    if(p == Q->rear) Q->rear = Q->front;
    free(p);
    return true;
}

4.5 链队列的释放

void Destroy_Link_queue(Link_Queue * Q){
    
    
    // 释放队列(该方法会保留数据元素的结点)
    while (Q->front != NULL){
    
    
        // 令尾指针指向第一个结点
        Q->rear = Q->front->next;
        // 每次释放一个结点
        free(Q->front);
        // 更新第一个结点
        Q->front = Q->rear;
    }
}

5.双端队列

双端队列是允许两端入队和出队的队列,可以认为是栈和队列的结合,元素的逻辑结构仍为线性结构。

双端队列按其性质,分为两类:

  • 输入受限: 可以从队列两端删除,但是只允许一端进行插入
  • 输出受限:可以从队列两端插入,但是只允许一端删除。

猜你喜欢

转载自blog.csdn.net/qq_41780234/article/details/127285212