双向链表的结点存储结构描述如下:
typedef struct Node
{
DataType data;
struct Node *next;
struct Node *prior;
}DListNode,*DLinkList;
双向链表的有些操作,如求链表的长度、查找链表的第i个结点等,与单链表中的算法实现基本没有什么差异。但是基于双向循环链表的插入和删除操作,因为涉及的前驱结点指针和后继结点指针,所以需要修改两个方向的指针。
(1)插入操作
插入操作就是在带头结点的双向循环链表中的第i个位置插入一个元素值为e的结点。插入成功返回1,否则返回0.算法思想是,首先找到第i个结点,用p指向该结点。在申请一个新结点,由s指向该结点,将e放入到数据域中。然后开始修改p和s指向的结点的指针域:修改s的prior域,使其指向p的直接前驱结点,即s->prior=p->prior;修改p的直接前驱结点的next域,使其指向s指向的结点,即p->prior->next=s;修改s的next域,使其指向p指向的结点,即s->next=p;修改p的prior域,使其指向s指向的结点,即p->prior=s。
代码实现如下:
int InsertDList(DLinkList head,int i,DataType e)
{
DListNode *p,*s;
int j=0;
p=head->next;
while(p!=head&&j<i)
{
p=p->next;
j++;
}
if (j!=i)
{
printf("插入位置不正确");
return 0;
}
s=(DListNode*)malloc(sizeof(DListNode));
if (!s)
{
return -1;
}
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return 1;
}
(2)删除操作
删除操作就是将带头结点的双向循环链表中的第i个结点删除。删除成功返回1,否则返回0.算法思想是,首先找到第i个结点,用p指向该结点。然后开始修改p指向的结点的直接前驱结点和直接后继结点的指针域,从而将p与链表断开。将p指向的结点与链表断开需要两步:第一步,修改p的前驱结点的next域,使其指向p的直接后继结点,即p->prior->next=p->next;第二步,修改p的直接后继结点的prior域,使其指向p的直接前驱结点,即p->next->prior=p->prior。代码实现如下:
int DeleteDList(DLinkList head,int i,DataType *e)
{
DListNode *p;
int j=0;
p=head->next;
while(p!=head&&j<i)
{
p=p->next;
j++;
}
if (j!=i)
{
printf("删除位置不正确");
return 0;
}
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return 1;
}
插入和删除操作的时间耗费主要在查找结点上,两者的时间复杂度都为O(n)。
注意:双向链表的插入和删除操作需要修改结点的prior和next域,比单链表操作要复杂些,因此要注意修改结点的指针域的顺序。