注:本文以造轮子为主,属于相对理论性、教学性的东西
理解:什么是队列?
你现在有一个放网球的竖球筒,你只能把球放到最上面,然后取出最下面的球,这就是队列。
先进先出,后进后出
类似于栈,我们通常实现以下几个功能:(由于本质上和栈很类似,多余的解释我就不做了)
- 初始化队列
- 入队
- 出队
- 取队头
具体代码
注:本代码没有包含main函数(我在其中加入了一些很具体的注释来帮助理解,如果喜欢读代码的朋友可以直接看)
注2:如果你不想纯粹读代码,该代码所有的具体解释请下移
#include<iostream>
#include<fstream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef char ElemType;
typedef int Status;
typedef struct QNode {
ElemType data;
struct QNode *next;
} QNode, *QueuePtr;
typedef struct {
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
} LinkQueue;
Status InitQueue(LinkQueue &Q) //构造一个空队列Q
{
Q.front = Q.rear = new QNode; //生成新结点作为头结点,队头和队尾指针指向此结点
Q.front->next = NULL; //头结点的指针域置空
return OK;
}
Status EnQueue(LinkQueue &Q, ElemType e) //入队:插入元素e为Q的新的队尾元素
{
QueuePtr p;
p = new QNode; //为入队元素分配结点空间,用指针p指向
p->data = e; //将新结点数据域置为e
p->next = NULL;
Q.rear->next = p; //将新结点插入到队尾
Q.rear = p; //修改队尾指针
return OK;
}
Status DeQueue(LinkQueue &Q, ElemType &e) //出队:删除Q的队头元素,用e返回其值
{
QueuePtr p;
if (Q.front == Q.rear)return ERROR; //若队列空,则返回ERROR
p = Q.front->next; //p指向队头元素
e = p->data; //e保存队头元素的值
Q.front->next = p->next; //修改头指针
if (Q.rear == p)
Q.rear = Q.front; //最后一个元素被删,队尾指针指向头结点
delete p; //释放原队头元素的空间
return OK;
}
ElemType GetHead(LinkQueue Q) //取队头元素:返回Q的队头元素,不修改队头指针
{
if (Q.front != Q.rear) //队列非空
return Q.front->next->data; //返回队头元素的值,队头指针不变
}
对于代码的具体解释
1,前设_宏定义
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef char ElemType;
typedef int Status;
没什么可说的。
2,前设_结构
typedef struct QNode {
ElemType data;
struct QNode *next;
} QNode, *QueuePtr;
typedef struct {
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
} LinkQueue;
QNode:用于存储结点,每一个结点有着一个数据域(data)和一个指针域(next)。
LinkQueue:用于存储链队列的头和尾。
3,函数_初始化
Status InitQueue(LinkQueue &Q) {
Q.front = Q.rear = new QNode;
Q.front->next = NULL;
return OK;
}
和栈不太相同的是,队列我们有着两个指针,头和尾。一般情况下,当我们的头尾指向同一个空结点的时候,代表着队列空了。
【作用解释】
对链队列进行初始化
思路:
- 创建一个新的结点,同时将头和尾都放到这里,此时新的结点为空(NULL)。
- 头结点的下一个结点同样为空。
4,函数_入队
Status EnQueue(LinkQueue &Q, ElemType e) {
QueuePtr p;
p = new QNode;
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
【作用解释】
在链队列的尾部加入一个新的数据e
思路:
- 为新的数据e创造空间(结点p),然后将它的下一个设为NULL(因为他在尾部,就是最后一个,他没有下一个)
QueuePtr p;
p = new QNode;
p->data = e;
p->next = NULL;
- 将原本尾的下一个改为当前的结点。(就是之前的最末尾现在尾部有了新的结点,所以它不再是尾部了)
Q.rear->next = p;
- 将当前的结点改为尾部。
Q.rear = p;
5,函数_出队
Status DeQueue(LinkQueue &Q, ElemType &e)
{
QueuePtr p;
if (Q.front == Q.rear)return ERROR;
p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front;
delete p;
return OK;
}
【作用解释】
输出队列的头部,并删除
思路:
- 判断队列是不是空的(如果队列首尾相连,就是空的。)
if (Q.front == Q.rear)return ERROR;
- 用一个值保存原本的头结点(便于之后删除清理空间),然后把我们要的数据给e
p = Q.front->next;
e = p->data;
- 修正头结点,并判断修正后是不是头尾相连(是不是空的)
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front;
- 删除旧的结点
delete p;
6,函数_取头结点
ElemType GetHead(LinkQueue Q)
{
if (Q.front != Q.rear)
return Q.front->next->data;
}
如果此时不是首尾相连(也就是不为空),则返回头结点下一个(也就是第一个结点)的数据域。
如果你是初学者:
试试写一个顺序队列,然后实现循环队列吧。没什么可写的了。
如果你想要没有注释的代码:
拿走,请。
#include<iostream>
#include<fstream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef char ElemType;
typedef int Status;
typedef struct QNode {
ElemType data;
struct QNode *next;
} QNode, *QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
} LinkQueue;
Status InitQueue(LinkQueue &Q)
{
Q.front = Q.rear = new QNode;
Q.front->next = NULL;
return OK;
}
Status EnQueue(LinkQueue &Q, ElemType e)
{
QueuePtr p;
p = new QNode;
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue &Q, ElemType &e)
{
QueuePtr p;
if (Q.front == Q.rear)return ERROR;
p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front;
delete p;
return OK;
}
ElemType GetHead(LinkQueue Q)
{
if (Q.front != Q.rear)
return Q.front->next->data;
}