队列的概念及相关术语:
队列是一种操作受限制的线性表,只允许在表的一端插入和删除元素。
对头(Front):允许删除的一端,又称队首;
队尾(Rear):允许插入的一段;
空队列:不含任何元素的空表;
向队列插入元素称为入队,删除元素称为出队或者离队。
队列的特性:
先进先出。
队列和栈一样不支持随机访问内部元素。
队列的存储结构:
- 顺序存储
- 普通顺序存储
- 循环队列
- 链式存储
队列的基本操作:
- Initqueue(&Q):初始化队列,构造一个空队列;
- QueueEmpty(Q):队列判空;
- Enqueue(&Q,x):入队;
- Dequeue(&Q):出队;
- GetHead(Q,x):读取对头元素;
1.1顺序存储--普通顺序队列
队列的存储类型定义:
typedef struct{
int data[maxsize];
int front,rear;
}queue;
队列的顺序存储实现是分配一块连续的存储单元存放队列中的元素。并设置两个指针:front和rear分别指向对头元素位置和队友元素位置,具体实现时,front和rear的具体指向有不同的方式:
- front指向对头元素,rear指向队尾元素的下一个位置
- front指向队友元素的前一个位置,rear指向队尾元素
不同的指向方式对于列表的操作影响不大,只是在一些小细节判断上有些许却别,本文以第一种凡是为例。
1.Initqueue(&Q):初始化队列,构造一个空队列:
void initqueue(queue &q) {
q.rear = q.front =0;
}
2.QueueEmpty(Q):队列判空:
bool isempty(queue q) {
if (q.rear == q.front) return true;
else return false;
}
3.Enqueue(&Q,x):入队:
void enqueue(queue &q,int x) {
if (isfull(q)) cout << "队列满" << endl;
else {
q.data[q.rear] = x;
q.rear++;
}
}
4.Dequeue(&Q):出队:
void dequeue(queue &q) {
if (isempty(q)) cout << "队列空" << endl;
else
{
q.front++;
}
}
5.GetHead(Q,x):读取对头元素:
void gethead(queue q) {
if (isempty(q)) cout << "队列空" << endl;
else {
cout<< q.data[q.front];
}
}
整体代码实现:
#include<iostream>
#define maxsize 10
using namespace std;
typedef struct{
int data[maxsize];
int front,rear;
}queue;
void initqueue(queue &q) {
q.rear = q.front =0;
}
bool isempty(queue q) {
if (q.rear == q.front) return true;
else return false;
}
bool isfull(queue q) {
if (q.rear == maxsize) return true;
else return false;
}
void enqueue(queue &q,int x) {
if (isfull(q)) cout << "队列满" << endl;
else {
q.data[q.rear] = x;
q.rear++;
}
}
void dequeue(queue &q) {
if (isempty(q)) cout << "队列空" << endl;
else
{
q.front++;
}
}
void gethead(queue q) {
if (isempty(q)) cout << "队列空" << endl;
else {
cout<< q.data[q.front];
}
}
int main() {
queue q;
initqueue(q);
for(int i = 0; i < 10; i++)
{
enqueue(q, i);
}
gethead(q);
cout << endl;
dequeue(q);
gethead(q);
return 0;
}
执行结果:
注意点:
由于是顺序存储的,在最开始就已经分配了一定大小的内存空间,所以必然要做判满和判空操作,当当前队列已经存满时,就不再往队列里插入元素。这里指的注意的是,判定为空的条件可以是q.front == q.rear。但是判空的条件不能是q.rear == maxsize,假设在某个时候,队列已经插满元素,这个时候去q.rear == maxsize,接下来,以此删除队列中一个元素,此时q.front ==1,此时队列显然不为空。这就造成了“假溢出”。
对于假溢出的概念,简单理解就是当前的队列的q.rear == maxsize但是,当前的队列并不满,有空间没有存入元素(0 -- (q.front-1)),这便造成大量的内存浪费。为解决这个问题,于是乎就有了下面的循环队列的概念。
1.2顺序存储--循环队列:
循环队列在物理任然是一个连续的内存空间,但是在逻辑上是一个环。
***关于循环对的详细逻辑讲解,我会在后续出一个视频讲解,点个关注,第一时间获取讲解***
那么此时,就可以用出发取余运算(%)来实现空间的循环利用并解决普通队列判满的缺陷。
具体实现如下:
- 初始化:Q.front == Q.rear == 0;
- 队首指针进1(出队):Q.front = (Q.front+1)%maxsize;
- 队尾指针进1(入队):Q.rear=(Q.rear+1)%maxsize;
- 队列长度:(Q.rear+maxsize-Q.front)%maxsize;
判空条件:Q.front == Q.rear
判满条件:(Q.rear+1)%maxsize==Q.front -- 利用牺牲一个单元来区分队列空和满。
1.Initqueue(&Q):初始化队列,构造一个空队列:
void initqueue(queue &q) {
q.rear = q.front =0;
}
2.QueueEmpty(Q):队列判空:
bool isempty(queue q) {
if (q.rear == q.front) return true;
else return false;
}
3.QueueFull(Q):队列判满:
bool isfull(queue q) {
if ((q.rear +1)%maxsize == q.front) return true;
else return false;
}
4.Enqueue(&Q,x):入队:
void enqueue(queue& q, int x) {
if (isfull(q)) cout << "队列满" << endl;
else {
q.data[q.rear] = x;
q.rear=(q.rear+1)%maxsize;
}
}
5.Dequeue(&Q):出队:
void dequeue(queue& q) {
if (isempty(q)) cout << "队列空" << endl;
else
{
q.front=(q.front+1)%maxsize;
}
}
6.GetHead(Q,x):读取对头元素:
void gethead(queue q) {
if (isempty(q)) cout << "队列空" << endl;
else {
cout<< q.data[q.front];
}
}
7.获取当前表长度:
int getlen(queue q)
{
return (q.rear + maxsize - q.front) % maxsize;
}
整体代码:
#include<iostream>
#define maxsize 10
using namespace std;
typedef struct {
int data[maxsize];
int front, rear;
}queue;
void initqueue(queue& q) {
q.rear = q.front = 0;
}
bool isempty(queue q) {
if (q.rear == q.front) return true;
else return false;
}
bool isfull(queue q) {
if ((q.rear + 1) % maxsize == q.front) return true;
else return false;
}
void enqueue(queue& q, int x) {
if (isfull(q)) cout << "队列满" << endl;
else {
q.data[q.rear] = x;
q.rear=(q.rear+1)%maxsize;
}
}
void dequeue(queue& q) {
if (isempty(q)) cout << "队列空" << endl;
else
{
q.front=(q.front+1)%maxsize;
}
}
void gethead(queue q) {
if (isempty(q)) cout << "队列空" << endl;
else {
cout << q.data[q.front]<<endl;
}
}
int getlen(queue q)
{
return (q.rear + maxsize - q.front) % maxsize;
}
int main() {
queue q;
initqueue(q);
for(int i = 0; i < maxsize-1; i++)
{
enqueue(q, i);
}
gethead(q);
dequeue(q);
gethead(q);
return 0;
}
执行结果:
2.链式存储的队列
链式存储的队列是一个同时代有对头指针和队尾指针的单链表。
同样的,单链表可以有头节点也可以没有头结点,在我之前的关于单链表的文章里讲述里有无头结点的区别。本文使用的带头结点的单链表,方便操作。
队列的链式存储类型定义:
typedef struct {
int data;
struct LinkNode* next;
}LinkNode;
typedef struct {
LinkNode* front, * rear;
}LinkQueue;
整体代码:
#include<iostream>
using namespace std;
typedef struct LinkNode {
int data;
struct LinkNode* next;
};
typedef struct LinkQueue{
LinkNode* front, * rear;
};
void initlinkqueue(LinkQueue &Q) {
LinkNode* head;
head = new LinkNode;
head->next = NULL;
Q.front = head;
Q.rear = head;
}
bool isempty(LinkQueue Q) {
if (Q.front == Q.rear) return true;
else return false;
}
void enqueue(LinkQueue& Q, int x) {
LinkNode* s = new LinkNode;
s->next = NULL;
s->data = x;
Q.rear->next = s;
Q.rear = s;
}
void dequeue(LinkQueue& Q) {
LinkNode* s;
s = Q.front->next;
Q.front->next = s->next;
delete[]s;
}
void destoryQueue(LinkQueue& Q) {
LinkNode* s = new LinkNode;
if (!isempty(Q)) cout << "空" << endl;
else {
while (Q.front->next != NULL)
{
s = Q.front->next;
Q.front = s->next;
delete[]s;
}
delete[]Q.front;
delete[]Q.rear;
}
}
int main() {
LinkQueue* Q;
Q = new LinkQueue;
initlinkqueue(*Q);
for (int i = 0; i < 10; i++)
{
enqueue(*Q, i);
}
cout << Q->front->next->data << endl;
dequeue(*Q);
cout << Q->front->next->data << endl;
destoryQueue(*Q);
return 0;
}
执行结果: