定义
deque(double-ended queue,双端队列)是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出。
结构示意图
首先每个节点包含一个存储的元素和一个指向下一节点的指针。整个双端队列则只包含一个头(head)节点的指针和一个尾(tail)节点的指针。头节点指向链表的第一个元素,尾节点指向最后一个元素。
定义的代码
struct Node{
T elem;
struct Node * next;
};
typedef struct Deque{
struct Node * head;
struct Node * tail;
}Deque;
需要实现的操作有:
头插(pushFront)、尾插(pushBack)、头出(popFront)、尾出(popBack)、获取头元素(peekFront)、获取尾元素(peekBack)、初始化、是否为空、获取大小、清空。
实现
这是初始化、是否为空、获取大小、清空的代码。简单说下。
初始化:头尾都置为NULL
是否为空:返回是否头部为空
获取大小:从头部开始遍历直到为空,记录size
清空:定义一个node和next节点,node用于记录当前即将删除的节点,next为下一个节点。每次循环需要先指定next节点再释放node。最后将队列que的head和tail置为空。
int initDeque(Deque * que){
assert(que != NULL);
que->head = NULL;
que->tail = NULL;
return SUCCESS;
}
bool emptyQueue(Deque * que){
assert(que != NULL);
return que->head != NULL;
}
size_t sizeQueue(Deque * que){
assert(que != NULL);
size_t size =0;
struct Node * node = que->head;
while(node != NULL){
++size;
node=node->next;
}
return size;
}
void clearQueue(Deque * que){
assert(que != NULL);
struct Node *node,*next;
for(que->head; node != NULL; node=next){
next=node->next;
free(node);
}
que->head=que->tail=NULL;
}
创建节点,插入需要用到的一个静态函数。传入节点的元素和下一个节点地址。先定义一个节点,初始化为动态申请内存,再将elem和next都赋予相应的对象。
static struct Node * createNode(T elem,struct Node *next){
struct Node *node=(struct Node *) malloc(sizeof(struct Node));
if(node!=NULL){
node->elem=elem;
node->next=next;
}
return node;
}
头插,先创建节点(注意传参的next要指定为que->head),再用此节点取代头节点。如果tail为空(代表此前que为空),也要使这个节点充当tail(因为现在只有这一个元素。)
int pushFrontQueue(Deque * que,T elem){
assert(que != NULL);
struct Node *node = createNode(elem,que->head);
if(node==NULL){
return FAILURE;
}
que->head=node;
if(que->tail==NULL){
que->tail=node;
}
return SUCCESS;
}
尾插,先创建一个节点,传参的next为空(在尾部插入,后面没有其他节点)。当tail不为空,将此节点设置为tail的next,当节点为空,此节点先取代head(que为空,需要兼任head),最后,取代tail(因为插入到尾部后此节点就成了新的尾部。)
int pushBackQueue(Deque *que,T elem)
{
assert(que!=NULL);
struct Node *node = createNode(elem,NULL);
if(node==NULL){
return FAILURE;
}
if(que->tail!=NULL){
que->tail->next=node;
}
else{
que->head=node;
}
que->tail=node;
return SUCCESS;
}
获取头节点,当head不为空,把head的elem赋值给*pelem。
int peekFirstQueue(Deque * que, T *pelem){
assert(que != NULL);
if(que->head==NULL){
return FAILURE;
}
if(pelem!= NULL){
*pelem=que->head->elem;
}
return SUCCESS;
}
获取尾节点,类似的道理,解释略。
int peekLastQueue(Deque * que, T *pelem){
assert(que != NULL);
if(que->tail==NULL){
return FAILURE;
}
if(pelem!=NULL){
*pelem=que->tail->elem;
}
return SUCCESS;
}
弹出头结点,可以用一个元素地址接受被弹出的元素。先判断que是否为空,是的话就不能弹出。记录head->next,释放head,再用记录的节点取代head。判断head是否为NULL(弹出后que是否为空),如果是,要把tail置为NULL。
int popFrontQueue(Deque * que,T* pelem){
assert(que!=NULL);
if(que->head==NULL){
return FAILURE;
}
if(pelem!=NULL){
*pelem=que->head->elem;
}
struct Node* node =que->head->next;
free(que->head);
que->head=node;
if(que->head==NUL){
que->tail=NULL;
}
return SUCCESS;
}
}
弹出尾节点,可以用一个元素地址去接受弹出的元素。先判断que是否为空,是的话就不能弹出。判断head等于tail的情况(只有一个节点),释放head,再将head和tail同时置NULL。当节点多于一个,就先用一个while循环找到tail的前一个节点,释放tail,将tail前一个节点作为tail。
int popBackQueue(Deque * que,T * pelem){
assert(que != NULL);
if(que->tail==NULL){
return FAILURE;
}
if(pelem!=NULL){
*pelem=que->tail->elem;
}
if(que->head==que->tail){
free(que->head);
que->head=que->tail=NULL;
}
else{
struct Node *node=que->head;
while(node->next!=que->tail){
node=node->next;
}
node->next=NULL;
free(que->tail);
que->tail=node;
}
return SUCCESS;
}