什么是双端队列?顾名思义就是可以在队列的两端进行插入和删除的队列。
这样的双端队列既可以当作普通的队列和栈来使用,还可以用在特定的情况下。
阅读本文需要先了解双向链表、队列和栈的知识。
先看一些提前做好的约定
#define ElemType int
typedef struct LinkNode{ // 每个结点双向链表类型的
ElemType data;
struct LinkNode *prior; // 前向指针
struct LinkNode *next; // 后向指针
}LinkNode;
typedef struct{ // 双端队列
LinkNode *front; // 队首指针 指向第一个队列的元素
LinkNode *rear; // 队尾指针 指向队列的最后一个元素
}LinkDeque;
首先要了解一下如何初始化和判断队列为空,如何判断队列为空往往是由初始化操作而定。
// 初始化
void DequeInit(LinkDeque &dq)
{
dq.front = dq.rear = NULL;
}
// 队列判空函数
bool DequeIsEmpty(LinkDeque dq)
{
return (dq.front == NULL) ? true : false;
}
既然是双端队列,那么肯定可以从两端都可以入队,那么怎么实现呢?其实我们在调用库函数的时候会发现有些函数会有一个true,false的参数选项,可填可不填用来区分两种情况。我们也借鉴这种方法。
// 入队,默认从队尾入队
bool DequePush(LinkDeque &dq, ElemType e, bool flag = true)
{
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
if(newNode == NULL){ // 没有申请到空间
return false;
}
newNode->data = e;
if(DequeIsEmpty(dq)){ // 如果队列为空
newNode->prior = NULL;
newNode->next = NULL;
dq.front = newNode;
dq.rear = newNode;
}
else if(flag == true){ // flag==true 从队尾插入
newNode->next = NULL; // 新插入的结点后面没有结点
newNode->prior = dq.rear;
dq.rear->next = newNode; // 先连接新的结点
dq.rear = newNode; // 后修改队尾指针
}
else{ // 从队首插入
newNode->next = dq.front;
newNode->prior = NULL; // 新插入的结点后面没有结点
dq.front->prior = newNode;
dq.front = newNode;
}
return true;
}
出队也是同理,但是出队的时候队列为空和队列中只有一个元素要做特殊处理。队列为空特判好理解,为什么只有一个元素也要特判呢?其实问题就出在删除操作上
LinkNode *tmp = dq.front;
dq.front = dq.front->next; -->这里如果只有一个元素的话,会使dq.front变成空指针
dq.front->prior = NULL; -->进而造成在这一步dq.front->prior非法访问
free(tmp);
// 出队,默认从队首出队
bool DequePop(LinkDeque &dq, bool flag = true)
{
if(DequeIsEmpty(dq)){ // 如果队列为空
return false;
}
else if(dq.front == dq.rear){ // 如果队列中只剩一个结点
LinkNode *tmp = dq.front;
dq.front = dq.rear = NULL;
free(tmp);
}
else if(flag == true){ // 从队首出队
LinkNode *tmp = dq.front;
dq.front = dq.front->next;
dq.front->prior = NULL;
free(tmp);
}
else{ // 从队尾出队
LinkNode *tmp = dq.rear;
dq.rear = dq.rear->prior;
dq.rear->next = NULL;
free(tmp);
}
return true;
}
完整代码:
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
typedef struct LinkNode{ // 每个结点双向链表类型的
ElemType data;
struct LinkNode *prior; // 前向指针
struct LinkNode *next; // 后向指针
}LinkNode;
typedef struct{
LinkNode *front; // 队首指针 指向第一个队列的元素
LinkNode *rear; // 队尾指针 指向队列的最后一个元素
}LinkDeque;
// 函数前置声明
bool DequeIsEmpty(LinkDeque dq);
// 初始化
void DequeInit(LinkDeque &dq)
{
dq.front = dq.rear = NULL;
}
// 入队,默认从队尾入队
bool DequePush(LinkDeque &dq, ElemType e, bool flag = true)
{
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
if(newNode == NULL){ // 没有申请到空间
return false;
}
newNode->data = e;
if(DequeIsEmpty(dq)){ // 如果队列为空
newNode->prior = NULL;
newNode->next = NULL;
dq.front = newNode;
dq.rear = newNode;
}
else if(flag == true){ // flag==true 从队尾插入
newNode->next = NULL; // 新插入的结点后面没有结点
newNode->prior = dq.rear;
dq.rear->next = newNode; // 先连接新的结点
dq.rear = newNode; // 后修改队尾指针
}
else{ // 从队首插入
newNode->next = dq.front;
newNode->prior = NULL; // 新插入的结点后面没有结点
dq.front->prior = newNode;
dq.front = newNode;
}
return true;
}
// 出队,默认从队首出队
bool DequePop(LinkDeque &dq, bool flag = true)
{
if(DequeIsEmpty(dq)){ // 如果队列为空
return false;
}
else if(dq.front == dq.rear){ // 如果队列中只剩一个结点
LinkNode *tmp = dq.front;
dq.front = dq.rear = NULL;
free(tmp);
}
else if(flag == true){ // 从队首出队
LinkNode *tmp = dq.front;
dq.front = dq.front->next;
dq.front->prior = NULL;
free(tmp);
}
else{ // 从队尾出队
LinkNode *tmp = dq.rear;
dq.rear = dq.rear->prior;
dq.rear->next = NULL;
free(tmp);
}
return true;
}
// 默认获取队首元素
ElemType GetElem(LinkDeque dq, bool flag = true)
{
if(DequeIsEmpty(dq)){
printf("双端队列为空");
exit(1);
}
return (flag == true) ? dq.front->data : dq.rear->data;
}
// 计算队列中的元素个数
int DequeSize(LinkDeque dq)
{
int length = 0;
LinkNode *p = dq.front;
while(p != NULL){
length++;
p = p->next;
}
return length;
}
// 队列判空函数
bool DequeIsEmpty(LinkDeque dq)
{
return (dq.front == NULL) ? true : false;
}
int main()
{
LinkDeque dq;
DequeInit(dq);
for(int i = 0; i < 10; i++){
DequePush(dq, i+1);
// printf("%d\n", GetElem(dq));
}
printf("length = %d\n", DequeSize(dq));
for(int i = 0; i < 10; i++){
printf("%d\n", GetElem(dq));
DequePop(dq);
printf("length = %d\n", DequeSize(dq));
}
return 0;
}