12. 队列
一、什么是队列
队列(quene) 是一种具有操作约束的线性表,它只能在一端插入,在另一端删除。队列的基本操作是 入队(Enquene)和 出队(Dequene)。入队,它是在 队尾(表的末端)插入一个元素。出队,它是删除并返回 队头(表的开头)的元素。队列具有 先进先出(FIFO)的特性。
ADT Queue
{
Data:
队列Q∈Queue, 队头元素Item∈ElementType;
Operation:
Queue CreateQueue(void); // 生成一个空的队列
int IsEmpty(Queue PtrQ); // 队列是否为空
void Enqueue(Queue PtrQ, ElementType Item); // 入队
ElementType Dequeue(Queue PtrQ); // 出队
ElementType Peek(Queue PtrQ); // 获取队头元素
} ADT Queue;
二、队列的顺序存储实现
2.1、队列的顺序存储
#define MAX_SIZE 10
typedef int ElementType;
typedef struct QNode
{
int Data[MAX_SIZE];
int Front;
int Rear;
}QNode, * Queue;
这里,队列,我们只使用
MAX_SIZE - 1
个元素,如果使用MAX_SIZE
元素,则Front == Rear
是无法判断队列是空的还是满的。如果使用MAX_SIZE - 1
个元素,则Front == Rear
是队列是空的,(Rear + 1) % MAX_SIZE == Front
时,队列是满的。
2.2、创建空的队列
/**
* @brief 创建一个空的队列
*
* @return Queue 创建的空的队列
*/
Queue CreateQueue(void)
{
Queue PtrQ = (Queue)malloc(sizeof(QNode));
memset(PtrQ->Data, 0, sizeof(ElementType) * MAX_SIZE);
PtrQ->Front = PtrQ->Rear = 0;
return PtrQ;
}
2.3、判断队列是否为空
/**
* @brief 判断队列是否为空
*
* @param PtrQ 队列
* @return int 0: 队列非空; 1: 队列为空
*/
int IsEmpty(Queue PtrQ)
{
return PtrQ->Front == PtrQ->Rear;
}
2.4、入队
/**
* @brief 入队
*
* @param PtrQ 队列
* @param Item 要入队的元素
*/
void Enqueue(Queue PtrQ, ElementType Item)
{
if ((PtrQ->Rear + 1) % MAX_SIZE == PtrQ->Front)
{
printf("队列已满!\n");
return;
}
PtrQ->Rear = (PtrQ->Rear + 1) % MAX_SIZE;
PtrQ->Data[PtrQ->Rear] = Item;
}
2.5、出队
/**
* @brief 出队
*
* @param PtrQ 队列
* @return ElementType 要出队的元素
*/
ElementType Dequeue(Queue PtrQ)
{
if (PtrQ->Front == PtrQ->Rear)
{
printf("队列为空!\n");
return NULL;
}
PtrQ->Front = (PtrQ->Front + 1) % MAX_SIZE;
return PtrQ->Data[PtrQ->Front];
}
2.6、获取队头元素
/**
* @brief 获取队头元素
*
* @param PtrQ 队列
* @return ElementType 队头元素
*/
ElementType Peek(Queue PtrQ)
{
if (PtrQ->Front == PtrQ->Rear)
{
printf("队列为空!\n");
return NULL;
}
return PtrQ->Data[PtrQ->Front + 1];
}
2.7、遍历队列
/**
* @brief 遍历队列
*
* @param PtrQ 队列
*/
void PrintQueue(Queue PtrQ)
{
if (PtrQ->Front == PtrQ->Rear)
{
return;
}
int i = PtrQ->Front + 1;
while (i != PtrQ->Rear)
{
printf("%d ", PtrQ->Data[i]);
i = (i + 1) % MAX_SIZE;
}
printf("%d\n", PtrQ->Data[PtrQ->Rear]);
}
三、队列的链式存储实现
3.1、队列的链式存储
队列也可以使用单链表进行链式存储,此时队头指针应该指向链表头,队尾指针指向链表尾。
typedef int ElementType;
typedef struct QNode
{
ElementType Data;
struct QNode *Next;
} QNode, Queue;
typedef struct PQueue
{
QNode * Front;
QNode * Rear;
}PQNode, * PQueue;
3.2、创建空的队列
/**
* @brief 创建一个空的队列
*
* @return Queue 创建的空的队列
*/
PQueue CreateQueue(void)
{
PQueue PtrQ = (PQueue)malloc(sizeof(PQNode));
PtrQ->Front = PtrQ->Rear = NULL;
return PtrQ;
}
3.3、判断队列是否为空
/**
* @brief 判断队列是否为空
*
* @param PtrQ 队列
* @return int 0: 队列非空; 1: 队列为空
*/
int IsEmpty(PQueue PtrQ)
{
return (PtrQ == NULL || PtrQ->Front == NULL);
}
3.4、入队
/**
* @brief 入队
*
* @param PtrQ 队列
* @param Item 要入队的元素
*/
void Enqueue(PQueue PtrQ, ElementType Item)
{
QNode * RearNode = malloc(sizeof(QNode));
RearNode->Data = Item;
RearNode->Next = NULL;
if (PtrQ->Front == NULL) // 若队列为空
{
PtrQ->Front = RearNode; // 队列头指针指向新节点
PtrQ->Rear = RearNode; // 队列尾指针指向新节点
}
else
{
PtrQ->Rear->Next = RearNode; // 原队列尾指针指向新节点
PtrQ->Rear = RearNode; // 队列尾指针指向新节点
}
}
3.5、出队
/**
* @brief 出队
*
* @param PtrQ 队列
* @return ElementType 要出队的元素
*/
ElementType Dequeue(PQueue PtrQ)
{
QNode * FrontNode = NULL;
ElementType FrontElement = 0;
if (PtrQ->Front == NULL)
{
printf("队列为空!\n");
return NULL;
}
FrontNode = PtrQ->Front;
if (PtrQ->Front == PtrQ->Rear) // 若队列只有一个元素
{
PtrQ->Front = PtrQ->Rear = NULL; // 删除后队列置为空
}
else
{
PtrQ->Front = PtrQ->Front->Next;
}
FrontElement = FrontNode->Data;
free(FrontNode);
return FrontElement;
}
3.6、获取队头元素
/**
* @brief 获取队头元素
*
* @param PtrQ 队列
* @return ElementType 队头元素
*/
ElementType Peek(PQueue PtrQ)
{
if (PtrQ->Front == NULL)
{
printf("队列为空!\n");
return NULL;
}
return PtrQ->Front->Data;
}
3.7、遍历队列
void PrintQueue(PQueue PtrQ)
{
QNode * Node = PtrQ->Front;
if (PtrQ->Front == NULL)
{
return;
}
while (Node != PtrQ->Rear)
{
printf("%d ", Node->Data);
Node = Node->Next;
}
printf("%d\n", Node->Data);
}