特点:插入只能在表尾(队尾)a1进行,删除只能在表头(队首)an进行。先进先出。
双端队列:允许同时从前后端添加和删除元素,是队列和栈的结合体。
输出受限双端队列:一端允许插入和删除,另一端只允许删除。
输入受限双端队列:一端允许插入和删除,另一端只允许插入。
超队列:插入只能在表尾进行,删除允许在两端进行。
存储方式:顺序队列,链队
一、顺序队列
建立空队列
#define MAXSIZE 5
typedef struct {
int data[MAXSIZE];
int f, r;//队首front和队尾rear指针,指向队首和队尾位置(元素下标)
}SeQueue;
SeQueue q;
//初始化,建立空队列
q.f = 0;
q.r = 0;
入队
void EnQueue(SeQueue q, int x) {
//第一个元素是data[0],入队后q.r指向data[1],即下一个要入队的地方
if (q.r < MAXSIZE) {
q.data[q.r] = x;
q.r++;
}
else printf("overflow");
}
出队
void DeQueue(SeQueue q) {
if (q.f!=q.r)//队不空,或者q.f<q.r
q.f++;
}
当入队和出队次数相同时,q.r和q.f指向同一个位置,满足判空条件,但此时队列明明还有位置(q.r < MAXSIZE),这种现象称为假溢出。
解决办法:循环队列,将顺序队列data[0...MAXSIZE-1]看成头尾相接的循环结构。但此时队空和队满的判断条件都变成了q.r==q.f,此时有两种解决方案。
1.牺牲一个存储位置,当(q.r+1)%MAXSIZE==q.f时,队满,q.r==q.f时队空。
2.设一个计数器,初始化清零,入队时计数器+1,出队时计数器-1。
以下是基本操作
#define MAXSIZE 5
typedef struct {
int data[MAXSIZE];
int f, r;//队首front和队尾rear指针,指向队首和队尾位置(元素下标)
}SeQueue;
SeQueue q;
//初始化,建立空队列
q.f = 0;
q.r = 0;
//入队
void EnQueue(SeQueue q, int x) {
if ((q.r + 1) % MAXSIZE != q.f)//有空间
{
q.data[q.r] = x;
q.r = (q.r + 1) % MAXSIZE;
}
else printf("overflow");
}
//出队
void DeQueue(SeQueue q) {
if (q.r != q.f)//队不空
q.f = (q.f + 1) % MAXSIZE;
}
//元素个数
int n = (q.r - q.f + MAXSIZE) % MAXSIZE;
二、链队
建立结构体维护队列
#define MAXQSIZE 5//最大队列长度
//声明一个结构体来表示节点
typedef struct Node{
int data; //节点数据域
struct Node* next; //节点指针域
}QueueNode;
//声明一个结构体来定义上面这个结构体的两个指针
typedef struct{
QueueNode* front, * rear;//分别指向队首和队尾的指针
}SqQueue;
建立空队列
void Init(SqQueue* s){
QueueNode* p;
p = (QueueNode*)malloc(sizeof(QueueNode));//为指向节点的指针分配空间
p->next = NULL;
s->front = s->rear = p;
}
判断队列是否为空
int Empty(SqQueue s)
{
if (s.front == s.rear)
{
return 1;//队空
}
return 0;
}
计算队列长度
int QueueLength(SqQueue Q) {
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}
//第二种写法,求队列长度
int GetLength(SqQueue s)
{
//声明一个节点类型的指针
QueueNode* p;
//让p指向队列的头指针
p = s.front;
//声明一个变量用来记录队列当前长度
int length = 0;
while (p->next)//当指针p所指的节点不为空时执行循环体
{
length++;
p = p->next;
}
return length;//返回当前队列的长度
}
入队
void EnQueue(SqQueue* s, int x){
//声明一个节点类型的指针变量用来存储要入队的元素
QueueNode* p;
p = (QueueNode*)malloc(sizeof(QueueNode));
if (!p) {
printf("内存分配失败\n\n");
return;
}
p->data = x; //指针指向的节点的数据域存放x
p->next = NULL; //指针指向的节点的指针域置为空
s->rear->next = p; //将队列的尾指针的指针域next指向指针p所指的节点
s->rear = p; //将队列的尾指针向后移一位,指向刚入队的节点的位置
}
获取队首元素
int GetTop(SqQueue s)
{
//首先判断队列是否为空
if (Empty(s))
{
printf("队列为空,无法获取队首元素\n\n");
return 0;
}
return s.front->next->data;//不为空的话就返回队首指针指向的第一个元素的数据域
}
出队
int DeQueue(SqQueue* s){
//先判断队列是否为空
if (Empty(*s))
{
printf("当前队列为空,无法执行出队操作\n\n");
return -1;
}
//用临时变量保存出队的元素
QueueNode* p;
p = s->front->next;
if (p == s->rear) //如果p指向了队尾节点
{
s->front = s->rear; //那么删除之后队首指针和尾指针指向同一个位置
}
int e = p->data;
s->front->next = p->next;//将队首指针指向第一个节点的下一个节点。
free(p);
return e;
}
主函数
{
int e;
SqQueue s;
Init(&s);
printf("当前队列是否为空,为空返回真1,不为空返回假0:%d\n\n", Empty(s));
printf("当前队列的长度为:%d\n\n", GetLength(s));
//入队
EnQueue(&s, 5);
EnQueue(&s, 4);
EnQueue(&s, 6);
EnQueue(&s, 2);
printf("当前队列是否为空,为空返回真1,不为空返回假0:%d\n\n", Empty(s));
printf("当前队列的长度为:%d\n\n", GetLength(s));
//获取当前队首元素
e = GetTop(s);
if (e) {
printf("当前队列的队首元素为:%d\n\n", e);
}
//执行出队操作
DeQueue(&s);
printf("出队的元素为:%d,当前队列的长度为:%d\n\n", e, GetLength(s));
//获取当前队首元素
e = GetTop(s);
if (e) {
printf("当前队列的队首元素为:%d\n\n", e);
}
//再执行一次出队操作
DeQueue(&s);
printf("出队的元素为:%d,当前队列的长度为:%d\n\n", e, GetLength(s));
//获取当前队首元素
e = GetTop(s);
if (e) {
printf("当前队列的队首元素为:%d\n\n", e);
}
//再执行一次出队操作
DeQueue(&s);
printf("出队的元素为:%d,当前队列的长度为:%d\n\n", e, GetLength(s));
//获取当前队首元素
e = GetTop(s);
if (e) {
printf("当前队列的队首元素为:%d\n\n", e);
}
return 0;
}