目录
方法二:设置标记位,入队只会出现队满的情况,出队只会出现队空的情况。因此当队头、队尾指针相同时,判断tag就可以知道队列的状况。
一、队列
队列(Queue)是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。
队列常见的基本操作
- InitQueue(& Q): 初始化队列
- QueueEmpty(Q): 判断队空
- EnQueue(&Q, x): 若队列Q未满,入队,将x加入,使之成为新的队尾
- DeQueue(&Q): 若队列Q非空,删除队头元素,并返回出队元素x
- GetHead(Q): 获取队头元素
队列可使用顺序存储或链式存储的方式来实现,接下来是不同物理存储结构下队列的基本操作实现。
二、 代码实现
1. 循环队列
循环队列的实现是通过顺序存储的方式来实现。那么为什么不叫做顺序队列呢,我们知道顺序存储实现是在内存中分配一块连续的存储单元存放队列中的元素,假设创建了一个存放10个存储单元的顺序队列,并进行一系列的出队、入队操作,观察rear、front指针的位置,如下图所示。
使用Q.rear == MAXSIZE可以作为队列满的条件吗?如上图所示,可以看到当队列中只有F元素,这时候仍满足条件,但很显然可以看出这是一种假的队列上溢出,在队列仍然存在空的位置可以存放元素,因而选择简单的顺序队列满足不了队列的特点,为了解决这个问题,我们可以设计一个循环队列,将出队元素重复使用,下面给出两种实现方法。
方法一:牺牲一个存储单元,目的是区分队空和队满
#include <stdio.h> #define ElemType int #define MAXSIZE 5 // 循环队列 牺牲一个结点的实现方式 typedef struct { ElemType data[MAXSIZE]; int rear, top; // rear 指向添加元素的位置,top指向队头指针 } SqQueue; // 初始化队列 void InitQueue(SqQueue &Q) { Q.rear = Q.top = 0; } // 判断队空 bool QueueEmpty(SqQueue Q) { return Q.rear == Q.top ? true : false; } // 判断队满 bool QueueFull(SqQueue Q) { return (Q.rear + 1) % MAXSIZE == Q.top ? true : false; } // 访问队头元素 ElemType GetTop(SqQueue Q) { if (! QueueEmpty(Q)) { return Q.data[Q.top]; } else { printf("当前队列已空!\n"); return NULL; } } // 入队 void EnQueue(SqQueue &Q, ElemType e) { if (QueueFull(Q)) { printf("当前队列已满!\n"); } else { Q.data[Q.rear] = e; Q.rear = (Q.rear + 1) % MAXSIZE; } } // 出队 void DeQueue(SqQueue &Q) { if (QueueEmpty(Q)) { printf("当前队列为空!\n"); } else { ElemType e = Q.data[Q.top]; Q.top = (Q.top + 1) % MAXSIZE; printf("成功出队,出队元素是%d\n", e); } } // 依次出队并打印元素 void DeQueueAll(SqQueue &Q) { while (! QueueEmpty(Q)) { DeQueue(Q); } } int main(void) { SqQueue Q; InitQueue(Q); EnQueue(Q, 1); EnQueue(Q, 2); EnQueue(Q, 3); EnQueue(Q, 4); DeQueueAll(Q); GetTop(Q); EnQueue(Q, 5); EnQueue(Q, 6); GetTop(Q); return 0; }
方法二:设置标记位,入队只会出现队满的情况,出队只会出现队空的情况。因此当队头、队尾指针相同时,判断tag就可以知道队列的状况。
#include <stdio.h>
#define MAXSIZE 10
typedef int ElemType;
// 循环队列
typedef struct {
ElemType data[MAXSIZE];
int rear, top;
int tag;
} SqQueue;
// 初始化队列
void InitQueue(SqQueue &Q) {
Q.rear = Q.top = 0;
Q.tag = -1;
}
// 判断队空
bool QueueEmpty(SqQueue Q) {
return (Q.rear == Q.top && Q.tag == 0);
}
// 判断队满
bool QueueFull(SqQueue Q) {
return (Q.rear == Q.top && Q.tag == 1);
}
// 入队
bool EnQueue(SqQueue &Q, ElemType e) {
if (QueueFull(Q)) {
printf("队满,无法添加元素!\n");
return false;
}
Q.data[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXSIZE;
Q.tag = 1;
return true;
}
// 出队
ElemType DeQueue(SqQueue &Q) {
if (QueueEmpty(Q)) {
printf("队空!\n");
return NULL;
}
ElemType e = Q.data[Q.top];
Q.top = (Q.top + 1) % MAXSIZE;
Q.tag = 0;
printf("出队的元素是%d\n", e);
return e;
}
// 遍历队列
void QueueTraverse(SqQueue Q) {
int top = Q.top;
if (QueueFull(Q)) {
int size = 10;
while (size-- > 0) {
printf("%d ", Q.data[top]);
top = (top + 1) % MAXSIZE;
}
}
while (top != Q.rear) {
ElemType e = Q.data[top];
printf("%d ", e);
top = (top + 1) % MAXSIZE;
}
printf("\n");
}
2. 链式队列
入队时,要注意尾指针的next设为空,防止野指针。
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
#define MAXSIZE 5
typedef struct LNode {
LNode *next;
ElemType data;
} LNode;
typedef struct {
LNode *rear, *top;
} LinkQueue;
// 初始化队列
void InitQueue(LinkQueue &Q) {
LNode *p = (LNode *) malloc(sizeof(LNode));
p->next = NULL;
Q.top = Q.rear = p;
}
// 入队
void EnQueue(LinkQueue &Q, ElemType e) {
LNode *p = (LNode *) malloc(sizeof(LNode));
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
printf("进队的元素是%d\n", Q.rear->data);
}
// 判断队空
bool QueueEmpty(LinkQueue Q) {
return Q.top->next == NULL ? true : false;
}
// 出队
void DeQueue(LinkQueue &Q) {
if (QueueEmpty(Q)) {
printf("当前队空!");
} else {
LNode *p = Q.top->next;
printf("出队的元素是%d\n", p->data);
Q.top->next = p->next;
if (Q.rear == p) {
Q.rear = Q.top;
}
free(p);
}
}
int main() {
LinkQueue Q;
InitQueue(Q);
EnQueue(Q, 4);
DeQueue(Q);
EnQueue(Q, 1);
DeQueue(Q);
DeQueue(Q);
return 0;
}
队列这种数据结构本质仍是一种线性表,对于链式队列的入队其实就是尾插法,如果线性表掌握很好的话,学习队列时也会很轻松。上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!