目录
E.入队操作,因为是链式存储,不需要考虑空间的问题,不需要判满。
个人的数据结构笔记,仅供参考,同时希望有大佬可以纠正我的错误
前言
数据结构中关于队列的基本认识以及队列的实现,当然这里只是我的个人见解,有不对的可以补充!!!
一、队列是什么?
定义:队列是一种“先进先出”的线性表。
a.队列只允许在队首删除数据元素,队尾添加数据元素。
b.基本分为 基于链表的队列(链队)和 基于数组的循环队列。
二,两种形式的实现
1.关于队列的一些基本操作
initQueue(&Q)//初始化操作
QueueEmtype(Q)//判空
QueueLength(Q)//获取队列的长度
getHead(Q)//获取队首元素
EnQueue(&Q)//入队操作
DeQueue(&Q)//出队操作
2.链队的实现
A.定义结构体部分
a.定义Node来作为链表的存储节点,包含数据域和指针域
b.定义LinkQueue,功能相当于锁定队列的头和尾,front指针来存储队列的首地址(头节点地址),rear指针来存储对尾结点。
typedef int QueueElemenType;
struct Node{
QueueElemenType data;//结点的数据域;
Node *next;//结点的指针域;
};
//构造一个Node类型的指针来锁定队的元素
//我的理解:
//将队列的头节点的地址保存在front指针中,存储的数据元素在front的的下一个结点中;
//将队列的尾结点的地址保存在rear指针中;
struct LinkQueue{
Node *front;//锁定队列的头部
Node *rear;//锁定队列的尾部;
};
B.初始化操作
//队的初始化操作;
//链式结构存储
void initQueue(LinkQueue &q){
q.front = new Node;//向存储系统申请一个空间
if(!q.front)
{
cout<<"error!"<<endl;
exit(0);
}
q.rear = q.front;//并将头指针和尾指针指向这一空间;
q.front->next = NULL;//此时对首结点为q,使其指针域指向NULL;
}
C.判空操作,链式存储不需要判满
bool QueueEmpty(LinkQueue q){
//队首队尾指向同一个空间,且头节点指向NULL
return q.front == q.rear && q.front->next==NULL;
}
D.获取队列长度和队首数据
int QueueLength(LinkQueue q){
Node *p = q.front->next;//类似于带头节点的链表从头结点的下一号位置开始遍历;
int length = 0;
while(p)
{
p = p->next;
length++;
}
return length;
}
//获取队首的值
QueueElemenType getHead(LinkQueue q,QueueElemenType &e){
if(QueueEmpty(q))
{
cout<<"Emtype!"<<endl;
exit(0);
}
e = q.front->next->data;//将q.front看作头节点;
return e;
}
E.入队操作,因为是链式存储,不需要考虑空间的问题,不需要判满。
//入队操作
//队列依循“先进先出”的定律,只在队尾增添元素;
void EnQueue(LinkQueue &q,QueueElemenType e){
Node *p;
p = new Node;
p->data = e;
p->next = NULL;
q.rear->next = p;//将新节点插入尾部
q.rear = p;//设立新的尾结点
}
F.出队操作
//出队操作
//队列的出队操作只在队首进行
1、判空;
2、申请新的节点,将对首数据赋给新的节点存储;
3、原来的队首退一步;
4、判断改变后队列中是否有元素,若无将首尾指向同一个节点
此时就会形成空间的浪费,引入循环链表解决该问题;
QueueElemenType DeQueue(LinkQueue &q)
{
QueueElemenType e;
if(QueueEmpty(q))//判空
{
cout<<"Emtype!"<<endl;
exit(0);
}
Node *p;
p = new Node;
p = q.front->next;//存储出队的结点
e = p->data;
q.front->next = p->next;//队首结点后退
if(p == q.rear)//若原有队列只含一个元素,则删除后队列为空
q.front = q.rear;
delete p;
return e;
}
main函数
个人的简单测试
int main()
{
LinkQueue Q;
initQueue(Q);
for(int i=1;i<6;i++)
{
EnQueue(Q,i);
}
int e=0;
cout<<getHead(Q,e)<<endl;
for(int i=0;i<5;i++)
{
cout<<DeQueue(Q)<<" ";
}
}
3、循环队列的实现
这里先介绍一下循环队列。
定义:为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。
这里的实现代码是基于数组实现的哦~
A.结构体的定义
基于数组实现,*base 是一个指向存储数组的指针,相当于Arry[n]中的Arry(个人愚见,便于理解),由于队列的插入与删除,分别只在队尾和队首进行,我们也只需要通过front,rear来记录队首和队尾的下标。
#define Maxsize 10
typedef int QElemetype;
struct SqQueue{
QElemetype *base;//存储数组的指针,base指针指向存储数组,可以通过front,rear来访问元素
int front;//数组第一个元素的下标
int rear;//数组最后一个元素的下标
};
B.初始化操作
void initQueue(SqQueue &q){
q.base = (QElemetype *)malloc(sizeof(QElemetype)*Maxsize);//引用malloc函数来实现,向系统申请连续空间的任务;
if(!q.base)//若空间申请失败
{
cout<<"error!!!"<<endl;
exit(0);
}
q.front = 0;//空间申请成功,front和rear标记同一个单元格
q.rear = 0;
}
C.判空、判满以及获取队列长度(这里的判空和判满我采取了tips里的第二种方法)
tips:判别空间满和空有种方式:一是设立一个标记来区别满还是空,二是舍弃了一个空间格防止“满”时front和rear也指向同一空间,约定队首指针在队尾指针的下一个位置时为“队列满”
//判空
bool QueueEmpty(SqQueue q){
return q.front == q.rear;//front 与 rear 相同时 数组为空
}
//判满
bool QueueFull(SqQueue q){
return (q.rear+1)% Maxsize == q.front;//循环数组最后一位元素的下一位为循环数组的头时,数组空间满了
}
//获取队列长度
int getLength(SqQueue q){
return (q.rear - q.front + Maxsize)%Maxsize;//rear不一定大于front
}
D.入队
void EnQueue(SqQueue &q,QElemetype e){
if(QueueFull(q))
exit(0);
q.base[q.rear] = e;//通过数组base[]来存储,依循队列只在最后一位元素后添加数据的原则,我们可以通过标记的q.rear来访问队尾
q.rear = (q.rear + 1)%Maxsize;//*同时注意base[q.rear]位置上始终为空,所以这里表示q.rear向后退了一步
}
E.出队
QElemetype DeQueue(SqQueue &q)
{
QElemetype e;
if(QueueEmpty(q))
exit(0);
e = q.base[q.front]; //通过数组base[]来存储,依循队列只在最后一位元素后添加数据的原则,我们可以通过标记的q.front来访问队首
q.front = (q.front+1)%Maxsize; // 释放掉一个数据后,队首不再是原来的的队首,向后退一格
return e;
}
个人测试代码
int main()
{
SqQueue s;
initQueue(s);
for(int i=0;i<5;i++)
{
EnQueue(s,i);
}
cout<<getLength(s)<<endl;
for(int i=0;i<10;i++)
{
cout<<DeQueue(s)<<" ";
}
}
总结
队列的两种基本形式差不多就是这样了哦~~~,写完跑路,溜了溜了~~~