数据结构之队列(C/C++实现)
定义
- 队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
- 队列是一种先进先出的线性表(FIFO),允许插入的一端称为队尾,允许删除的一端称为队头
队列的抽象数据类型
ADT 队列(Queue)
Data
同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
InitQueue(*Q): 初始化操作,建立一个空队列Q
DestroyQueue(*Q): 若队列Q存在,则销毁它
ClearQueue(*Q): 将队列Q清空
QueueEmpty(Q): 若队列为空,返回true,否则返回false
GetHead(Q,*e): 若队列存在且非空,用e返回队列Q的队头元素。
EnQueue(*Q,e): 若队列Q存在,插入新元素e到队列Q中并称为队尾元素
DeQueue(*Q,*e): 删除队列Q中的队头元素,并用e返回其值
QueueLength(Q): 返回队列Q的元素个数
endADT
循环队列
- 队列顺序储存,引入两个指针front和rear。front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear时,队列为空。
- 队列的顺序存储会出现假溢出现象
- 为了解决假溢出现象,我们引入循环队列。我们把队列这种头尾相连的顺序存储结构称为循环队列
循环队列为空的时候为front等于rear 循环队列满的时候front也等于rear。
那么如何判断此队列究竟是空还是满呢?
- 设置一个标志变量flag,当front == rear,且flag=0时队列为空,当front==rear,且flag=1时队列满
- 当队列空时,条件就是front==rear,当队列满时,我们修改其条件,保留一个元素空间。也就是说,队列满时,数组中还有一个空闲单元。
针对第二种情况:
- 队列满的条件是(rear+1)%QueueSize == front
- 队列长度计算公式(rear-front+QueueSize)% QueueSize
#define MAXSIZE 50
#define OK 1
#define ERROR 0
#define True 1
#define False 0
typedef int Status;/*函数的返回类型,这里定义为int*/
typedef int QElemType;/*SElemType根据情况来顶,这里假设为int*/
typedef struct
{
QElemType data[MAXSIZE];
int front; //头指针
int rear; //尾指针,若队列不为空,指向队列元素的下一个位置
}SqQueue;
循环队列的初始化:
//初始化一个空队列
Status InitQueue(SqQueue *Q)
{
Q->front=0;
Q->rear=0;
return OK;
}
循环队列求队列长度
//返回Q的元素个数,也就是队列的长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
循环队列的入队操作
//若队列未满,插入元素e为Q新的队列元素
Status EnQueue(SqQueue *Q,QElemType e)
{
if((Q->rear+1)%MAXSIZE==Q->front) /*队列满的判断*/
{
return ERROR;
}
Q->data[Q->rear]=e; //将元素e赋值给队列
Q->rear=(Q->rear+1)%MAXSIZE; //rear指针向后移动一个位置,若到最后则转到数组头部
return OK;
}
循环队列的出队操作:
//若队列不空,则删除Q中的队头元素,用e返回其值
Status DeQueue(SqQueue *Q,QElemType *e)
{
if(Q->front==Q->rear)
{
return ERROR;
}
*e=Q->data[Q->front]; //将队头元素赋值给e
Q->front=(Q->front+1)%MAXSIZE; //front指针向后移一位,若到最后则转到数组头部
return OK;
}
队列的链式存储结构及实现
队头指针指向链队列的头节点
队尾指针指向链队列的尾节点
空队列时,front和rear都指向头节点。
链队列的结构:
typedef struct QNode /*结点结构*/
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct /*队列的链表结构*/
{
QueuePtr front,rear; /*队头,队尾指针*/
}LinkQueue;
入队操作:
/*插入元素e为Q的新的队尾元素*/
Status EnQueue(LinkQueue *Q,QElemType e)
{
QueuePtr s=(QueuePtr)malloc(sizeof (QNode));
if(!s) /*存储分配失败*/
{
exit(1);
}
s->data=e;
s->next=nullptr;
Q->rear->next=s; //把拥有元素e新节点s赋值给原队尾节点的后继
Q->rear=s; //把当前的s设置成队尾节点,rear指向s
return OK;
}
出队操作:
/*若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR*/
Status DeQueue(LinkQueue *Q,QElemType *e)
{
QueuePtr p;
if(Q->front==Q->rear)
{
return ERROR;
}
p=Q->front->next; //将欲删除的队头结点暂存给p
*e=p->data; //将欲删除的队头结点的值赋给e
Q->front->next=p->next; //将原队头节点后继p->next赋值给头节点后继
if(Q->rear==p) //若队头是队尾,则删除后将rear指向头节点
{
Q->rear=Q->front;
}
free(p);
return OK;
}
循环队列的C++实现
#include <iostream>
using namespace std;
class queue
{
public:
queue(int items);//参数items为打算存放的数据量
virtual ~queue();
bool EnQueue(int data);
bool DeQueue(int* pRecvData=nullptr);
void DeTraverse();//遍历队列中存储的数据元素
int GetSize();//获取队列中的数据量
bool IsFull();
bool IsEmpty();
private:
int* pArr;
int head,tail;
int length;//队列的实际长度
};
queue::queue(int items)
{
this->length = items + 1;//必须多开辟一个空间,因为尾指针要指向这个空间
pArr = new int[this->length];
head = tail = 0;
}
queue::~queue()
{
delete[]pArr;
}
bool queue::EnQueue(int data)
{
if (IsFull())
{
cout<<"队列已满!"<<endl;
return false;
}
pArr[tail] = data;
tail = (tail + 1) % length;
return true;
}
bool queue::DeQueue(int* pRecvData)
{
if (IsEmpty())
{
cout << "队列为空!" << endl;
return false;
}
if (pRecvData != nullptr)
*pRecvData = pArr[head];
head = (head+1) % length;
return true;
}
int queue::GetSize()
{
return (tail - head + length) % length;
}
bool queue::IsFull()
{
return (tail + 1) % length == head;
}
bool queue::IsEmpty()
{
return head == tail;
}
void queue::DeTraverse()
{
if (head == tail)
{
cout << "队列为空!" << endl;
return;
}
for (int i = head; i != tail; i = (i + 1) % length)
cout << pArr[i] << " ";
cout << endl;
}
int main()
{
queue que(5);
cout<<"que的数据量为:" <<que.GetSize()<< endl;
que.EnQueue(1);
que.EnQueue(2);
que.EnQueue(3);
que.EnQueue(4);
que.EnQueue(5);
que.DeQueue();
que.EnQueue(6);
cout << "que的数据量为:" << que.GetSize() << endl;
que.DeTraverse();
getchar();
return 0;
}