数据结构:基于链式存储的双端队列

什么是双端队列?顾名思义就是可以在队列的两端进行插入和删除的队列。

这样的双端队列既可以当作普通的队列和栈来使用,还可以用在特定的情况下。

阅读本文需要先了解双向链表、队列和栈的知识。

先看一些提前做好的约定

#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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章