【摘要】前一篇博客阐述了循环单链表,它虽然能够实现从任一结点出发沿着链能找到其前驱结点,但时间耗费是O(n)。如果希望从表中快速确定某一个结点的前驱,另一个解决方法就是在单链表的每个结点里再增加一个指向其前驱的指针域prior。这样形成的链表中就有两条方向不同的链,我们可称之为双(向)链表(Double Linked List)。所以本文主要讨论的是双向循环链表基本操作,包括创建、删除、插入、打印等等,欢迎大家批评指正错误。
1、存储结构
与单链表类似,双链表一般也是有头指针唯一确定的,增加头结点也能使双链表的某些运算变得方便。同时双向链表也可以有循环表,称为双向循环链表,其结构如图2.15所示。
由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。设指针p指向双链表中某一结点,则有下式成立:
P->prior->next=p=p->next->prior
在双向链表中,那些只涉及后继指针的算法,如求表长度、取元素、元素定位等,与单链表中相应的算法相同,但对于前插和删除操作则涉及到前驱和后继两个方向的指针变化,因此与单链表中的算法不同。
算法描述:欲在双向链表第i个结点之前插入一个新的结点,则指针的变化情况如图2.16所示。
2、参考代码
// Double_List.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*存储结构*/
typedef struct _node {
void *data;
struct _node *prior;//前驱指针
struct _node *next;//后继指针
}NODE;
/*循环链表*/
typedef struct DoubleLinkList {
NODE *head;//前驱结点
NODE *last;//后继结点
int length;//链表长度
}DLList;
DLList *CreateList(); // 创建双链表
int AppendList(DLList *l, void *data, int size);// 在链表后追加结点
int InsertList(DLList *l, int pos, void *data, int size);//在 pos 位置的元素前插入
int DeleteNode(DLList *l, int pos, void *e, int size);// 删除位置为pos的结点,并用e返回被删除的结点数据
void PrintList(DLList *l, void (*printnode)(void *));// 打印
void ClearList(DLList *l);// 清空链表
void DestoryList(DLList **l);// 销毁链表
double d[5] = {10.23, 34.23, 54.65, 122, 35.5};//插入的数据
void PrintData(void *data)
{
double *d = (double *)data;
printf("d = %lf\n",*d);
}
int main()
{
int i;
double e;
DLList *list = CreateList();
for (i = 0; i < 4; ++i){
AppendList(list, &d[i], sizeof(d[i]));
}
printf("原始数据是:\n");
PrintList(list,PrintData);
printf("\n\n");
printf("插入数据:\n");
InsertList(list, 2, &d[4], sizeof(d[4]));//插入元素
PrintList(list,PrintData);
printf("\n\n");
DeleteNode(list, 1, &e, sizeof(double));
printf("被删除的元素是:");
printf("%lf\n\n",e);
DestoryList(&list);
if (NULL == list)
printf("list is NULL\n");
else
printf("list not NULL\n");
getchar();
return 0;
}
// 创建双链表
DLList *CreateList()
{
DLList *l = (DLList *)malloc(sizeof(DLList));
if (NULL == l)
exit(0);
l->head = (NODE *)malloc(sizeof(NODE)); // 设置哨兵结点,可方便双链表的操作
if (NULL == l->head)
exit(0);
memset(l->head, 0, sizeof(NODE));
l->last = (NODE *)malloc(sizeof(NODE));
if (NULL == l->last)
exit(0);
memset(l->last, 0, sizeof(NODE));
l->head->next = l->last;
l->last->prior = l->head;
l->length = 0;
return l;
}
// 在链表后追加结点
int AppendList(DLList *l, void *data, int size)
{
NODE *p = NULL;
if (NULL == l || NULL == data)
return 0;
p = (NODE *)malloc(sizeof(NODE));
if (NULL == p)
return 0;
p->data = malloc(size);
if (NULL == p->data){
free(p);
return 0;
}
memcpy(p->data, data, size);//拷贝数据
/*顺序特别重要*/
p->next = l->last;
p->prior = l->last->prior;
l->last->prior->next = p;
l->last->prior = p;
l->length++;
return 1;
}
// 在 pos 位置的元素前插入
int InsertList(DLList *l, int pos, void *data, int size)
{
NODE *p = NULL, *q = NULL;
int i = 0;
if (NULL == l || NULL == data || pos < 1 || pos > l->length)
return 0;
p = (NODE *)malloc(sizeof(NODE));
if (NULL == p)
return 0;
p->data = malloc(size);
if (NULL == p->data){
free(p);
return 0;
}
memcpy(p->data, data, size);
q = l->head;
while (i < pos - 1){
q = q->next;
++i;
}
/*顺序特别重要*/
p->next = q->next;
q->next = p;
p->prior = q;
p->next->prior = p;
l->length++;
return 1;
}
// 删除位置为pos的结点,并用e返回被删除的结点数据
int DeleteNode(DLList *l, int pos, void *e, int size)
{
NODE *p = NULL, *q = NULL;
int i = 0;
if (NULL == l || pos < 1 || pos > l->length)
return 0;
p = l->head;
while (i < pos - 1){ // 这里遍历到了pos位置的前一个结点,还可以直接遍历到pos位置的结点上,通过prior操作删除
p = p->next;
}
q = p->next;
memcpy(e, q->data, size);
/*顺序特别重要*/
p->next = q->next;
q->next->prior = p;
free(q->data);
free(q);
l->length--;
return 1;
}
// 打印
void PrintList(DLList *l, void (*printnode)(void *))
{
NODE *p = NULL;
int i = 0;
if (NULL == l || NULL == printnode)
return;
p = l->head;
while (i < l->length){
p = p->next;
printnode(p->data);
++i;
}
}
// 清空链表
void ClearList(DLList *l)
{
NODE *p = NULL;
if (NULL == l->head->next)
return;
p = l->head->next;
l->head->next = p->next;
free(p);
if (l->head->next != l->last) // 如果p的下一个结点不是尾结点,防止删掉尾结点
ClearList(l); // 这里用递归
}
// 销毁链表
void DestoryList(DLList **l)
{
DLList *p = *l;
if (NULL == p)
return;
ClearList(p);
free(p->head); // 释放哨兵结点
free(p->last);
free(p);
*l = NULL;
}