像栈一样,队列也是表。
队列是具有一定操作约束的线性表。
特点
插入和删除操作:只能从表的一端插入,另一端删除。
数据插入:入队
数据删除:出队
先进先出:FIFO
与其他的表类似,对队列的操作也有一下五种。
1.Create Queue(int MaxSize) 创建队列
2.IsFullQ (Queue Q, int MaxSize)判断队列是否已满
3.AddQ (Queue Q, ElementType item)插入数据
4.IsEmptyQ (Queue Q)判断队列是否为空
5.ElementType DeleteQ(Queue Q) 将队头数据元素删除并返回
队列的插入删除 数组图解
最开始Front,Rear都指向-1
插入一个数据元素,Rear+1,Front不变
继续做类似插入操作
删除操作:在头部进行删除,Front+1,Rear不变
当操作进行到这一步,数据将无法插入,但是数组前面还有空间,这样就造成了空间的浪费,解决的方法是用循环来实现队列
实现图解
但是用循环实现的方法需要注意的是判断队列是否已满或队列为空的条件。
初始情况下,我们认为队列为空的条件是Front == Rear。但是随着插入和删除操作的进行,我们会发现,当队列满时也会出现Front ==Rear 的情况。
出现这种情况的原因可以简要概括为我们是用Front和Rear的“差距”来判断队列是否空满。 Front和Rear的“差距”有0,1,2,3,4,5 六种情况,但是数组中队列装载元素个数的情况有:0(空队列),1,2,3,4,5,6(满队列)七种情况。用六种情况去判定七种存在的情况显然是不可能的。
所以解决的方式可以是
1.增加一个额外的标记Flag来记录队列装载元素的情况,插入或删除。
2.仅使用n-1个数组空间,使队列装载元素的情况和Front,Rear“差距”的情况相等。
代码实现
数组实现
结构体定义
#define MaxSize<存储数据的最大个数>
typedef struct{
ElementType Data[MaxSize];
int rear; //队列尾元素位置
int front; //队列头元素位置
}Queue;
入队列
void AddQ (Queue *PtrQ, ElementType item){
if((PtrQ->rear+1) % MaxSize == PtrQ->Front){ //判断队列是否已满
printf("队列已满!");
return;
}
//改变rear的位置,因为使用循环,故对MaxSize取余
PtrQ->rear=(PtrQ->rear+1) % MaxSize;
Ptr->Data[Ptr->rear] = item; //插入数据
}
出队列
ElementType DeleteQ(Queue *PtrQ){
if((PtrQ->Front) == (Ptr->rear)){ //判断队列是否为空
printf("队列为空!");
return ERROR;
}
else{
//改变Front的位置,由于循环,故对MaxSize取余
PtrQ->Front = (PtrQ->Front+1) % MaxSize;
return PtrQ->Data[PtrQ->Front+1];
}
}
循环数组,源代码/C++
#include<iostream>
#include<cstdlib>
using namespace std;
typedef struct QueueRecord
{
int Capacity;
int Front;
int Rear;
int size;
int *Array;
}Queue;
//定义全局变量
int MaxElements = 10;
int MinQueueSize = 25;
Queue CreateQueue();
void DisposeQueue(Queue Q);
Queue EnQueue(int X, Queue Q);
Queue DeQueue(Queue Q);
//判断队列是否为空
int IsEmpty(Queue Q)
{
return Q.size == 0;
}
//判断队列是否已满
int IsFull(Queue Q)
{
return Q.size == Q.Capacity;
}
int main()
{
Queue Q;
Q = CreateQueue();
int i = 1;
int X;
while(i)
{
cout << "Please enter 1 to EnQueue, 2 to DeQueue, 0 to end:" << endl;
cin >> i;
if(i == 1)
{
cout << "Please Input the Element:" << endl;
cin >> X;
Q = EnQueue(X, Q);
}
if(i == 2)
{
Q = DeQueue(Q);
}
}
return 0;
}
Queue CreateQueue()
{
if(MaxElements >= MinQueueSize)
{
cout << "This Queue is too samll" << endl;
exit(1);
}
else
{
Queue Q;
Q.Array = (int*)malloc(sizeof(int)*MaxElements);
if(Q.Array == NULL)
{
cout << "Out of space!!" << endl;
exit(0);
}
Q.Capacity = MaxElements;
Q.Front = 0;
Q.Rear = 0;
Q.size = 0;
return Q;
}
}
void DisposeQueue(Queue Q)
{
if(Q.Array != NULL)
{
free(Q.Array);
}
}
Queue EnQueue(int X, Queue Q)
{
if(!IsFull(Q))
{
if(Q.size == 0)
{
Q.Array[Q.Rear] = X;
Q.size++;
return Q;
}
else
{
Q.Rear = (Q.Rear+1)%(Q.Capacity-1);
Q.Array[Q.Rear] = X;
Q.size++;
return Q;
}
}
else
{
cout << "This Queue is Full" << endl;
DisposeQueue(Q);
exit(1);
}
}
Queue DeQueue(Queue Q)
{
if(!IsEmpty(Q))
{
cout << Q.Array[Q.Front] << endl;
Q.Front = (Q.Front+1)%(Q.Capacity-1); //若超过数组大小,回到数组开头
Q.size--;
return Q;
}
else
{
cout << "This Queue is Empty" << endl;
DisposeQueue(Q);
exit(1);
}
}
链表实现
对队列的操作是两端。在使用单向链表时,选择哪边作为Front,哪边作为Rear尤为重要。
在链表的头部做插入删除都很方便,但是如果要在末尾进行删除操作,删除了当前结点,Front无法返回,Front 的值就丢失了。故只能在链表的头部进行删除操作,链表的尾部进行插入操作。
图解
代码实现
结构体定义
typedef struct Node{
ElementType Data;
struct Node *next;
}QNode;
//用一个单独的结构体来存储队列的头元素和尾部元素
typedef struct{
QNode *rear; //指向队列尾元素位置
QNode *front; //指向队列头元素位置
}LinkQueue;
出队列
ElementType DeleteQ(LinkQueue *PtrQ)
{
QNode *FrontCell;
ElementType FrontElem;
if(PtrQ->front == NULL)
{
printf("队列为空");
return ERROR;
}
FrontCell = PtrQ->front;
if(PtrQ->front == PtrQ->rear) //若队列只有一个元素
PtrQ->front = PtrQ->rear = NULL; //将队列置为空
else
PtrQ->front = PtrQ->front->Next; //改变front指针的指向
FrontElem = FrontCell->Data;
free(FrontCell); //释放被删除的结点
return FrontElem;
}