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.双端队列
双端队列是允许两端入队和出队的队列,可以认为是栈和队列的结合,元素的逻辑结构仍为线性结构。
双端队列按其性质,分为两类:
- 输入受限: 可以从队列两端删除,但是只允许一端进行插入
- 输出受限:可以从队列两端插入,但是只允许一端删除。