13. 双端队列
一、什么是双端队列
双端队列是只允许从两端插入和删除的线性表。
ADT Deque
{
Data:
双端队列D∈Deque, 队头元素Item∈ElementType;
Operation:
Deque CreateDeque(void); // 生成一个空的双端队列
int IsEmpty(Deque PtrD); // 双端队列是否为空
void OfferFirst(Deque PtrD, ElementType X); // 队头添加元素
void OfferLast(Deque PtrD, ElementType X); // 队尾添加元素
ElementType PollFirst(Deque PtrD); // 队头删除元素
ElementType PollLast(Deque PtrD); // 队尾删除元素
ElementType PeekFirst(Deque PtrD); // 查看队头元素
ElementType PeekLast(Deque PtrD); // 查看队尾元素
} ADT Deque;
二、双端队列的顺序存储实现
2.1、双端队列的顺序存储
#define MAX_SIZE 10
typedef int ElementType;
typedef struct DNode
{
int Data[MAX_SIZE];
int Front;
int Rear;
}DNode, * Deque;
这里,队列,我们只使用
MAX_SIZE - 1
个元素,如果使用MAX_SIZE
元素,则Front == Rear
是无法判断队列是空的还是满的。如果使用MAX_SIZE - 1
个元素,则Front == Rear
是队列是空的,(Rear + 1) % MAX_SIZE == Front
时,队列是满的。
2.2、创建空的双端队列
/**
* @brief 创建一个带头节点的双端队列
*
* @return Deque 指向双端队列的指针
*/
Deque CreateDeque(void)
{
Deque PtrD = (Deque)malloc(sizeof(DNode));
memset(PtrD->Data, 0, sizeof(ElementType) * MAX_SIZE);
PtrD->Front = PtrD->Rear = 0;
return PtrD;
}
2.3、判断双端队列是否为空
/**
* @brief 判断双端队列是否为空
*
* @param PtrD 双端队列
* @return int 0: 非空; 1: 空
*/
int IsEmpty(Deque PtrD)
{
return PtrD->Front == PtrD->Rear;
}
2.4、队头插入元素
/**
* @brief 队头插入元素
*
* @param PtrD 双端队列
* @param X 插入的元素
*/
void OfferFirst(Deque PtrD, ElementType X)
{
if ((PtrD->Rear + 1) % MAX_SIZE == PtrD->Front)
{
printf("队列已满!\n");
return;
}
PtrD->Data[PtrD->Front] = X;
PtrD->Front = (PtrD->Front - 1 > 0) ? (PtrD->Front - 1) : (MAX_SIZE - 1);
}
2.5、队尾插入元素
/**
* @brief 队尾插入元素
*
* @param PtrD 双端队列
* @param X 插入的元素
*/
void OfferLast(Deque PtrD, ElementType X)
{
if ((PtrD->Rear + 1) % MAX_SIZE == PtrD->Front)
{
printf("队列已满!\n");
return;
}
PtrD->Rear = (PtrD->Rear + 1) % MAX_SIZE;
PtrD->Data[PtrD->Rear] = X;
}
2.6、队头删除元素
/**
* @brief 删除队头元素
*
* @param PtrD 双端队列
* @return ElementType 队头元素
*/
ElementType PollFirst(Deque PtrD)
{
if (PtrD->Front == PtrD->Rear)
{
printf("队列为空!\n");
return NULL;
}
PtrD->Front = (PtrD->Front + 1) % MAX_SIZE;
return PtrD->Data[PtrD->Front];
}
2.7、队尾删除元素
/**
* @brief 删除队尾元素
*
* @param PtrD 双端队列
* @return ElementType 队尾元素
*/
ElementType PollLast(Deque PtrD)
{
ElementType x = 0;
if (PtrD->Front == PtrD->Rear)
{
printf("队列为空!\n");
return NULL;
}
x = PtrD->Data[PtrD->Rear];
PtrD->Rear = (PtrD->Rear - 1 > 0) ? (PtrD->Rear - 1) : (MAX_SIZE - 1);
return x;
}
2.8、查看队头元素
/**
* @brief 查看队头元素
*
* @param PtrD 双端队列
* @return ElementType 队头元素
*/
ElementType PeekFirst(Deque PtrD)
{
if (PtrD->Front == PtrD->Rear)
{
printf("队列为空!\n");
return NULL;
}
return PtrD->Data[(PtrD->Front + 1) % MAX_SIZE];
}
2.9、查看队尾元素
/**
* @brief 查看队尾元素
*
* @param PtrD 双端队列
* @return ElementType 队尾元素
*/
ElementType PeekLast(Deque PtrD)
{
if (PtrD->Front == PtrD->Rear)
{
printf("队列为空!\n");
return NULL;
}
return PtrD->Data[PtrD->Rear];
}
2.10、遍历双端队列
/**
* @brief 遍历双端队列
*
* @param PtrD 双端队列
*/
void PrintDeque(Deque PtrD)
{
if (PtrD->Front == PtrD->Rear)
{
return;
}
int i = PtrD->Front + 1;
while (i != PtrD->Rear)
{
printf("%d ", PtrD->Data[i]);
i = (i + 1) % MAX_SIZE;
}
printf("%d\n", PtrD->Data[PtrD->Rear]);
}
三、双端队列的链式存储实现
3.1、双端队列的链式存储
typedef int ElementType;
typedef struct DNode
{
ElementType Data;
struct DNode * Prior;
struct DNode * Next;
} DNode, * Deque;
3.2、创建空的双端队列
/**
* @brief 创建一个带头节点的双端队列
*
* @return Deque 指向双端队列的指针
*/
Deque CreateDeque(void)
{
Deque PtrD = (Deque)malloc(sizeof(DNode));
PtrD->Data = 0;
PtrD->Next = PtrD->Prior = PtrD;
return PtrD;
}
3.3、判断双端队列是否为空
/**
* @brief 判断双端队列是否为空
*
* @param PtrD 双端队列
* @return int 0: 非空; 1: 空
*/
int IsEmpty(Deque PtrD)
{
return PtrD->Next == PtrD;
}
3.4、队头插入元素
/**
* @brief 队头插入元素
*
* @param PtrD 双端队列
* @param X 插入的元素
*/
void OfferFirst(Deque PtrD, ElementType X)
{
Deque NewNode = (Deque)malloc(sizeof(DNode));
NewNode->Data = X;
NewNode->Next = PtrD->Next;
NewNode->Prior = PtrD;
PtrD->Next->Prior = NewNode;
PtrD->Next = NewNode;
}
3.5、队尾插入元素
/**
* @brief 队尾插入元素
*
* @param PtrD 双端队列
* @param X 插入的元素
*/
void OfferLast(Deque PtrD, ElementType X)
{
Deque NewNode = (Deque)malloc(sizeof(DNode));
NewNode->Data = X;
NewNode->Next = PtrD;
NewNode->Prior = PtrD->Prior;
PtrD->Prior->Next = NewNode;
PtrD->Prior = NewNode;
}
3.6、队头删除元素
/**
* @brief 删除队头元素
*
* @param PtrD 双端队列
* @return ElementType 队头元素
*/
ElementType PollFirst(Deque PtrD)
{
Deque FirstNode = PtrD->Next;
ElementType X = FirstNode->Data;
if (PtrD == NULL || PtrD->Next == PtrD)
{
printf("双端队列已空!\n");
return NULL;
}
PtrD->Next = FirstNode->Next;
FirstNode->Next->Prior = PtrD;
free(FirstNode);
return X;
}
3.7、队尾删除元素
/**
* @brief 删除队尾元素
*
* @param PtrD 双端队列
* @return ElementType 队尾元素
*/
ElementType PollLast(Deque PtrD)
{
Deque LastNode = PtrD->Prior;
ElementType X = LastNode->Data;
if (PtrD == NULL || PtrD->Next == PtrD)
{
printf("双端队列已空!\n");
return NULL;
}
PtrD->Prior = LastNode->Prior;
LastNode->Prior->Next = PtrD;
free(LastNode);
return X;
}
3.8、查看队头元素
/**
* @brief 查看队头元素
*
* @param PtrD 双端队列
* @return ElementType 队头元素
*/
ElementType PeekFirst(Deque PtrD)
{
if (PtrD == NULL || PtrD->Next == PtrD)
{
printf("双端队列已空!\n");
return NULL;
}
return PtrD->Next->Data;
}
3.9、查看队尾元素
/**
* @brief 查看队尾元素
*
* @param PtrD 双端队列
* @return ElementType 队尾元素
*/
ElementType PeekLast(Deque PtrD)
{
if (PtrD == NULL || PtrD->Next == PtrD)
{
printf("双端队列已空!\n");
return NULL;
}
return PtrD->Prior->Data;
}
3.10、遍历双端队列
/**
* @brief 遍历双端队列
*
* @param PtrD 双端队列
*/
void PrintDeque(Deque PtrD)
{
if (PtrD == NULL || PtrD->Next == PtrD)
{
return;
}
Deque P = PtrD->Next;
while (P != PtrD)
{
printf("%d ", P->Data);
P = P->Next;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报