3-2 队列
1、基本概念
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行删除操作的端称为队头 ,进行插入操作的端称为队尾。
FIFO (First In First Out) 先进先出
队列的基本操作:
①init() 置空,将队列Q初始化为空;
②empty() 判队列空,
③size() 返回队列中元素的个数
④front() 取队头元素,若队列未空,则函数返回队头 数据元素。
⑤rear() 取尾头元素,若队列未空,则函数返回队尾 数据元素。
⑥push(x) 入队列,若队列未满,在原队尾后加入数据元素x,使x成为新的队尾元素;
⑦pop() 出队列,若队列未空,则将队列的队头元素删除。
队列存储结构的实现有以下两种方式:顺序队列 、 链式队列
2、顺序队列
在顺序表的基础上实现的队列结构;
一般情况下,rear等于max时,说明队尾元素应占据了顺序表的最后一个存储位置,即无法再从队尾插入新元素,
这个时候判定为“队满”,但是如果说队首元素前面还有空的位置,那么说明我们这个顺序表还是有空间可以存放元素的,
所以rear == max 称为 “假溢出”,那么只要改动一下判别“队满”的条件,就可以把顺序队列 当作 循环队列 来使用了:
①头文件:
#ifndef SQ_QUEUE_H_
#define SQ_QUEUE_H_
#define MAX 10
struct sq_queue {
int data[MAX];
int front;//队首标记
int rear;//队尾标记
};
void Init(sq_queue *q);
bool Empty(sq_queue *q);
int Len(sq_queue *q);
bool Full_SQ(sq_queue *q);
bool Full_Circle(sq_queue *q);
int Front(sq_queue *q);
int Rear(sq_queue *q);
int Pop(sq_queue *q);
void Push_sq(sq_queue *q, int x);
void Push_circle(sq_queue *q, int x);
void Show(sq_queue *q);
#endif // !SQ_QUEUE_H_
②函数定义文件:
#include<iostream>
#include"sq_queue.h"
using std::cin;
using std::cout;
using std::endl;
/*初始化*/
void Init(sq_queue *q) {
q->front = q->rear = 0;
}
/*判空*/
bool Empty(sq_queue *q) {
if (q->front == q->rear)
return true;
else
return false;
}
/*返回长度*/
int Len(sq_queue *q) {
//这个公式对于循环队列 和 非循环普通队列都适用
return (q->rear - q->front + MAX) % MAX;
}
/*普通顺序队列的 判满条件, 会有假溢出现象*/
bool Full_SQ(sq_queue *q) {
if (q->rear == MAX)
return true;
else
return false;
}
/*循环顺序队列的 判满条件, 能利用front前面的空间存储元素*/
bool Full_Circle(sq_queue *q) {
if ((q->rear + 1) % MAX == q->front)
return true;
else
return false;
}
int Front(sq_queue *q) {
if (Empty(q))
return NAN;
else
return q->data[q->front];
}
int Rear(sq_queue *q) {
if (Empty(q))
return NAN;
else
return q->data[q->rear-1];//由于rear指向队尾元素的下一个位置,所以这里要 -1
}
int Pop(sq_queue *q) {
if (Empty(q))
return NAN;
else
return q->data[q->front++];
}
void Push_sq(sq_queue *q, int x) {
/*普通顺序队列的情况*/
if (Full_SQ(q))
return;
else
q->data[q->rear++]=x;
}
void Push_circle(sq_queue *q, int x){
/*循环队列的情况*/
if (Full_Circle(q))
return;
else {
q->rear = q->rear % MAX;
q->data[q->rear] = x;
q->rear++;
}
}
void Show(sq_queue *q) {
if (Empty(q))
return;
else {
for (int i = q->front; i < MAX; i++)
cout << q->data[i] << " ";
if (q->rear < q->front)
for (int j = 0; j < q->rear; j++)
cout << q->data[j] << " ";
}
cout << endl;
}
③主函数文件:
#include<iostream>
#include"sq_queue.h"
using std::cin;
using std::cout;
using std::endl;
int main() {
sq_queue q1;
Init(&q1);
cout << "\nthe queue is empty: " << Empty(&q1) << " and its length is: " << Len(&q1) << endl;
int i = 0;
cout << "\n先将所有位置都填上元素:\n";
//先用顺序队列的判满条件
while (!Full_SQ(&q1)) {
Push_sq(&q1, i);
i++;
//cout << q1.front <<" "<<q1.rear<< endl;
//getchar();
}
Show(&q1);
cout << "\n然后将队首的3个元素依次出队列:\n";
Pop(&q1); Show(&q1);
Pop(&q1); Show(&q1);
Pop(&q1); Show(&q1);
cout << "\n根据普通顺序队列的判满条件,它是一个满队列:Full_SQ =" << Full_SQ(&q1) << endl;
cout << "\n根据循环队列的判满条件,它不是一个满队列:Full_Circle = " << Full_Circle(&q1) << endl;
cout << "\n所以如果使用循环队列的判满条件,我们可以继续入队列,添加元素:\n";
//再用循环队列的判满条件
while (!Full_Circle(&q1)) {
Push_circle(&q1, i);
i++;
}
Show(&q1);
cout << "\n但是能存储的元素的数目要比普通顺序队列少一个\n";
cin.get();
return 0;
}
3、链式队列
采用链表来实现队列,我还是选用带头结点的单链表来实现,因为其在第一个位置删除时,不用改变头指针;
我们在插入的时候,选择在链表尾部使用尾插法插入,所以链表尾部视为队尾rear;
在删除时,删除头结点后的首结点,为了方便起见,我们可以把头结点认为是front,这样front就一直不会变化,比较方便
如图所示:
①头文件
#ifndef LINK_QUEUE_H_
#define LINK_QUEUE_H_
/**/
/**/
/*结点的定义*/
typedef struct Link_list_node {
int data;
struct Link_list_node *next;
}node;
/*链式队列结构定义*/
typedef struct link_queue {
node * front;
node * rear;
}lq;
void Init(lq * q);
bool Empty(lq * q);
void Destroy(lq *q);
int Size(lq *q);
int Front(lq *q);
int Rear(lq *q);
int Pop(lq *q);
void Push(lq *q, int x);
void Show(lq *q);
#endif // !LINK_QUEUE_H_
②函数定义文件
#include<iostream>
#include"link_queue.h"
using std::cin;
using std::cout;
using std::endl;
void Init(lq * q) {
/**/
q->front = new node;
q->front->next = nullptr;
q->rear = q->front;
}
bool Empty(lq * q) {
if (q->front == q->rear)
return true;
else
return false;
}
void Destroy(lq *q) {
node *p;
while (q->front != nullptr) {
p = q->front->next;
delete q->front;
q->front = p;
}
q->rear = nullptr;
}
int Size(lq *q) {
node *p = q->front;
int j = 0;
while (p->next != nullptr) {
p = p->next;
j++;
}
return j;
}
int Front(lq *q) {
if (Empty(q))
return NAN;
else
return q->front->next->data;
}
int Rear(lq *q) {
if (Empty(q))
return NAN;
else {
node *p = q->front;
while (p->next != nullptr)
p = p->next;
return p->data;
}
}
int Pop(lq *q) {
if (Empty(q))
return NAN;
else {
//创建一个结点指向队首元素
node *p = q->front->next;
//将队首元素从链表中断开
q->front->next = p->next;
//存储此队首元素的值
int x = p->data;
//如果删除的这第一个元素恰好是rear,就需要更新rear的值为front,即空队列的情况
if(p==q->rear)
q->rear = q->front;
delete p;
return x;
}
}
void Push(lq *q, int x) {
//创建一个结点接受新的值
node *s = new node;
s->data = x;
s->next = nullptr;
//将其插入链表的最后1个位置,即采用尾插法
/*传统思路是通过遍历的方式找到最后一个结点*/
/*
node *p = q->front;
while (p->next != nullptr) {
p = p->next;
}
p->next = s;
*/
/*但是我们这里已经有最后一个结点的地址了,那就是rear!*/
q->rear->next = s;;
q->rear = s;
}
void Show(lq *q) {
node*p = q->front;
while (p->next != nullptr) {
p = p->next;
cout << p->data << " ";
}
cout << endl;
}
③主函数文件
#include<iostream>
#include"link_queue.h"
using std::cin;
using std::cout;
using std::endl;
int main() {
lq q1;
Init(&q1);
cout << "\nthe queue is empty: " << Empty(&q1) << " and its length is: " << Size(&q1) << endl;
int x = 0;
cout << "\n在队尾插入元素,按 q 表示结束:\n";
while (cin>>x) {
Push(&q1, x);
cout << "the queue is: ";
Show(&q1);
}
cin.clear();
while (cin.get() != '\n')
continue;
cout << "\n在队头弹出元素\n";
while (!Empty(&q1)) {
cout<<"\ndelete: "<<Pop(&q1);
cout << "\nthe queue is: ";
Show(&q1);
}
Destroy(&q1);
cin.get();
return 0;
}
如有错误,请不吝指出,谢谢