目录
基于上一篇的单向链表学习后,现在来学习双向链表,很多算法都是一样的,只是双向处理多了一个指针,把单向链表的各个算法搞清楚,双向和循环自然好理解
一、双向链表
1.创建链表:
结构体比单项多了一个指针
node_t *creat_doublist(void)
{
node_t *head = malloc(sizeof(node_t));//堆上开空间
if(head ==NULL)//创建失败
{
printf("malloc fail");
return NULL;
}
head->next =NULL;//创建成功,此时是空链表
head->prev =NULL;
return head;
}
2.头插法:
堆上开好空间后,先把新节点的前后指针链接好后,再断开头节点和新节点后面节点的链接
注意:需要判断新节点后面是否还有节点

//头插法
void doublist_insert_head(node_t *phead,data_t data)//返回结构体类型的指针
{
//step1:创建一个新节点并给值
node_t *pnew = malloc(sizeof(node_t));//堆上开空间
if(pnew ==NULL)//创建失败
{
printf("malloc fail");
return ;
}
pnew->prev = NULL;
pnew->next = NULL;
pnew->data =data;
//插入新节点
pnew->next = phead->next;
pnew->prev = phead;
//断开之前的链接,指向新节点
phead->next= pnew;
if(pnew->next !=NULL)
pnew->next->prev = pnew;
}
3.尾插法:
尾插只需要遍历到尾节点链接就行
//尾插法
void doublist_insert_tail(node_t *phead,data_t data)//返回结构体类型的指针
{
//step1:创建一个新节点并给值
node_t *pnew = malloc(sizeof(node_t));//堆上开空间
if(pnew ==NULL)//创建失败
{
printf("malloc fail");
return ;
}
pnew->prev = NULL;
pnew->next = NULL;
pnew->data =data;
node_t *p = phead;
while(p->next !=NULL )
{
p = p->next;
}
pnew->next = p->next;
pnew->prev = p;
p->next = pnew;
}
4.打印链表:
和单向链表差不多,多了一个可以逆序打印的功能:
//打印链表
int printf_doublist(node_t *phead,int dir)
{
if(is_empty(phead)==1)//检查头节点指针域是否为空
{
printf("empty!\n");
return -1;
}
node_t *p = phead->next;
if(dir ==1)
{
while(p!=NULL)//节点到达空地址时结束,
//注意不是节点的指针域为空的时候结束
{
printf("%d ",p->data);
p = p->next;
}
}else
{
while(p->next!=NULL)
{
p = p->next;
}
while(p!= phead)
{
printf("%d ",p->data);
p = p->prev;
}
}
putchar('\n');
return 0;
}
5.头删法:
先链接头和首节点的下一个节点,再删除首节点
注意:需要判断首节点的后面是否还有节点
6.尾删法:
遍历到最后一个节点之后,将它的前一个节点的指针域置为NULL即可
//尾删法
int doublist_delete_tail(node_t *phead)
{
if(is_empty(phead)==1)//检查头节点指针域是否为空
{
return -1;
}
node_t *p = phead->next;
while(p->next!= NULL)
{
p = p->next;
}
p->prev->next = NULL;
free(p);
return 0;
}
7.查找删除:
现在遍历找到,在循环里面判断找值,找到后使用头删法

//查找删除
int doublist_delete_key(node_t *phead,data_t k)
{
if(is_empty(phead)==1)//检查头节点指针域是否为空
{
return -1;
}
node_t *p = phead->next;
while(p!= NULL)
{
if(p->data == k)
{
p->prev->next = p->next ;
if(p->next !=NULL)
p->next->prev = p->prev;
free(p);
return 0;
}
p = p->next;
}
return -1;
}
销毁、长度计算、查找和修改数值算法与单向链表完全一致,这里不再说明。
二、循环链表(难)
和单向链表相同,但尾节点指针域指向首节点
算法需要用到do-while
1.创建链表:
头节点的指针域指向自己

node_t *creat_circlist(void)
{
node_t *head = malloc(sizeof(node_t));//堆上开空间
if(head ==NULL)//创建失败
{
printf("malloc fail");
return NULL;
}else
{
printf("success!\n");
}
head->next =head;//创建成功,此时是空链表
return head;
}
2.头插(难):
开堆后,把头节点与新节点相连,再把末尾节点的指针域链接到新节点
难点:
1.末尾节点需要定义一个指针去遍历链表找到它
2.链表只有单个节点时需要把指针域指向自己

//头插法
node_t *insert_circlist_head(node_t * phead,data_t data)
{
if(phead ==NULL)
{
return NULL;
}
node_t *pnew = malloc(sizeof(node_t));//堆上开空间
if(pnew ==NULL)//创建失败
{
printf("malloc fail");
return NULL;
}
pnew->next = NULL;
pnew->data =data;
//链接
pnew->next = phead->next;
node_t *p = phead->next;
while(p->next != phead->next)
{
p = p->next;
}
p->next = pnew;
if(pnew->next ==phead)
{
pnew->next = pnew;
}
return pnew;
}
3.打印:
和单向链表打印一样,只是循环结束条件变为p遍历到首届点,
难点:
需要用到do-while,如果使用while,遍历到最后之后无法再打印数据,而是跳出循环
//打印
void printf_circlist(node_t *phead)
{
if(is_empty(phead)==1 || is_empty(phead) == -1 )
{
printf("empty\n");
return ;
}
node_t *p = phead->next;
do
{
printf("%d ",p->data);
p = p->next;
}while(p!= phead->next);//用do-while可以先执行再判断
putchar('\n');
return;
}
4.头删法:
需要用到两个指针,一个是首节点,一个需要遍历找到尾节点,先 链接头节点的指针域和尾节点的指针域,再断开首节点即可
易错点:先后顺序一定不能搞错
void circlist_delete_head(node_t *phead)
{
if(is_empty(phead)==1 || is_empty(phead) == -1 )
{
return ;
}
node_t *p = phead->next;
node_t *ptemp = phead->next;
do
{
p = p->next;
}while(p->next != phead->next);//用do-while先执行再判断
phead->next = ptemp->next;
p->next = ptemp->next;
free(ptemp);
return ;
}
5.查找:
和单向链表一样,只是跳出循环的条件变了
node_t *circlist_find(node_t *phead,data_t k)
{
if(is_empty(phead)==1 || is_empty(phead) == -1 )
{
return NULL;
}
node_t *p = phead->next;
do
{
if(p->data == k)
{
return p;
}
p = p->next;
}while(p!= phead->next);//用do-while先执行再判断
return NULL;
}
6.销毁:
要从首节点的下一个节点开始删,循环删完后需要再free掉这个最后的节点,同时不要忘记free头节点,和头节点指针域置为NULL
易错点:忘记处理头节点

//销毁
void circlist_destory(node_t **phead)
{
if(is_empty(*phead)==1 || is_empty(*phead) == -1 )
{
return ;
}
node_t *p = (*phead)->next->next;
do{
node_t *ptemp = p;
p = p->next;
free(ptemp);
}while(p->next != (*phead)->next);
free(p);
free(*phead);
*phead = NULL;
return;
}


