与栈一样,队列也是一种限定性的数据结构,即限定其插入操作只能在线性表的一端进行,而访问和删除操作只能在线性表的另一端进行。
限定只能在线性表的一端进行插入操作,在另一端进行访问和删除操作的线性表称为队列,将进行插入操作(入队)的一端称为队尾,可以进行访问和删除操作(出对)的一端称为队头。队列也称为先进先出(FIFO)线性表。
链队列
用带头结点的链表表示的队列称为链队列,在链队列中,队头为链表的第一个元素,队尾为链表的最后一个元素,因此出队操作在队头进行,而入队操作在队尾进行。
链队列的front为链表的头结点,队头为front的下一个结点,方便出队;rear和队尾都是链表的最后一个节点,方便入队。
#include<iostream>
using namespace std;
typedef int datatype;
constexpr auto N = 10;
typedef struct clNode {
datatype data;
clNode* next;
}*cqNode;
//链队列
struct chainQueue {
cqNode front; //头结点
cqNode rear; //队尾
chainQueue() :front(NULL), rear(NULL) {
}
};
//判断队列cp是否为空,如果为空,返回true,否则返回false
bool empty(chainQueue cq) {
return cq.front == NULL;
}
//入队:在链队列cq的队尾添加一个元素
void push(chainQueue& cq, datatype x) {
cqNode tmp = new clNode;
tmp->data = x;
tmp->next = NULL;
if (empty(cq)) {
cq.front = new clNode;
cq.front->next = tmp; //链队列中有一个新加入的元素
}else {
cq.rear->next = tmp; //新加入的结点当前链队列队尾的后面
}
cq.rear = tmp; //队尾始终为新加入的结点
}
//获取链队列cq队头元素的值
datatype front(chainQueue cq) {
if (empty(cq)) {
cout << "队列为空!" << endl;
return -1;
}
return cq.front->next->data;
}
//出队:删除链队列cq的队头
void pop(chainQueue& cq) {
if (empty(cq)) {
cout << "队列为空!" << endl;
return;
}
cqNode tmp = cq.front->next; //队头结点,将被删除
cq.front->next = tmp->next;
delete tmp; tmp = NULL;
if (cq.front->next == NULL) { //将链队列中的元素都删除
delete cq.front;
cq.front = NULL;
cq.rear = NULL;
}
}
//遍历cq的所有元素,同时清空链队列cq
void cq_traverse(chainQueue& cq) {
while (!empty(cq)) {
cout << front(cq) << " ";
pop(cq);
}
}
int main()
{
chainQueue cq1;
push(cq1, 5);
push(cq1, 34);
push(cq1, 55);
push(cq1, 56);
cq_traverse(cq1);
}
循环队列
循环队列的实现基于顺序表,队头为顺序表的第一个有效元素,队尾为顺序表的最后一个元素。其将表示队列的数组看成一个环,即下标最大的元素的下一个元素的下标为0,这样可以避免假溢出现象。
假溢出现象:假设下标0和1的位置空闲(已出队),rear以达到最大,即此时的数组sq并未填满,成为“假溢出”。
#include<iostream>
using namespace std;
typedef int datatype;
constexpr auto N = 6;
//对于队列sq入队操作,需要先将sq.rear向后移一位,而出队操作需要将sq.front向后移一位
//但是当他们的值都为N时,需要调整为0,简化操作,设置以下宏定义
#define next(x) ((x)==N-1?0:(x)+1)
//循环队列的类型定义
typedef struct sqNode {
datatype data[N];
int front;
int rear;
sqNode() :front(0), rear(0) {
memset(data, 0, sizeof(data));
}
}seqQueue;
//判断队列sq是否为空
//当队列为空时,front = rear = 0,每当插入元素尾指针+1,删除元素是头指针-1
//为了达到区别队空or队满,可以通过牺牲一个存储空间来实现
bool empty(seqQueue sq) {
return sq.rear == sq.front;
}
//判断队列sq是否为满
bool full(seqQueue sq) {
return next(sq.rear) == sq.front;
}
//入队:在队列sq的队尾添加一个元素,元素的值为x
void push(seqQueue& sq, datatype x) {
if (full(sq)) {
cout << "队列已满,入队失败!" << endl;
return;
}
sq.data[sq.rear] = x;
sq.rear = next(sq.rear);
}
//获取队列sq的队头元素,作为函数的返回值
datatype front(seqQueue sq) {
if (empty(sq)) {
cout << "队列为空,获取队头元素失败" << endl;
return -1;
}
return sq.data[sq.front];
}
//出队:删除sq的队头元素
void pop(seqQueue& sq) {
if (empty(sq)) {
cout << "队列为空,出队失败!" << endl;
return;
}
sq.front = next(sq.front);
}
//遍历sq,并清空sq
void sq_traverse(seqQueue& sq) {
while (!empty(sq)) {
cout << front(sq) << " ";
pop(sq);
}
}
int main()
{
seqQueue sq1;
push(sq1, 5);
push(sq1, 55);
push(sq1, 89);
push(sq1, 3);
push(sq1, 26);
/*sq_traverse(sq1);
cout << endl;
sq_traverse(sq1);*/
front(sq1);
//pop(sq1);
//sq_traverse(sq1);
//push(sq1, 99);
sq_traverse(sq1);
}