之前写了一篇基于C语言的单向链表,顺便也把双向链表写了。双向链表跟单向链表的区别就是多了一个向前的指针域。老实说,到目前为止我还没有真正体会到双向链表带给我的好处和优势。。。。
之前写的单链表链接:https://blog.csdn.net/tiramisu_L/article/details/107549773
双向链表的结构如下,两个指针域
typedef struct _list
{
struct _list *prev;
struct _list *next;
int data;
}list;
根据《数据结构与算法分析》这本的介绍,双向链表是没有头节点的,关于头节点的个人理解在我单链表的文章上有说到,可以去看一下。然后有一个需要注意的点,第一个节点的prev指针要指向NULL,最后一个节点的next要指向NULL

双链表的一些操作跟单链表没有太多差别,这里具体实现一下
1、创建节点
list *Create_List_Node(int data)
{
list *pNode = (list *)malloc(sizeof(list));
pNode->data = data;
pNode->prev = NULL;
pNode->next = NULL;
return pNode;
}
2、插入节点,插入节点分头插和尾插
1)、尾插法。尾插法很简单,只要遍历到末尾,把末尾的指针的next指向新的节点,然后把新的节点的prev指向末尾节点即可
list *Add_List_Node_Tail(list *pHead, list *pNode)
{
if (pHead == NULL || pNode == NULL)
{
printf("pHead is null or pNode is null\n");
return NULL;
}
while (pHead->next != NULL)
{
pHead = pHead->next;
}
pHead->next = pNode; //末尾节点的next指向新节点
pNode->prev = pHead; //新节点的prev直线末尾节点
return pHead;
}
2)、头插法。这里的头插法是指在第一个节点后面插入新的节点。头插法一般是需要4个步骤来完成节点插入,步骤是有一定顺序的,如果顺序出错是没办法正常插入的。下面来具体说一下要怎么确定这个顺序

首先需要注意的是,上面的第一个节点是pHead这个只是习惯命名,你完全可以用其他名字来代替。上面的顺序只是其中一种下面还会介绍另外一种
首先是要先把Node1的prev指针域指向新节点pNode,即Node1->prev = pNode。但是我们是不知道Node1的,我们只能通过pHead来知道Node1,也就是pHead->next = Node1,那么就有pHead->next->prev = pNode;
第二,我们需要用pNode的next来指向Node1,也就是pNode->next = Node1;同理我们是不知道Node1的,但是我们知道pHead->next = Node1;所以我们就会有pNode->next = pHead->next;
第三,直接就是pHead->next = pNode;
第四,直接就是pNode->prev = pHead;
这里再介绍另外一种方式
第一, 直接pNode->prev = pHead;
第二,pNode的next指向Node1,即pNode->next = Node1,但是我们不知道Node1,可是我们有pHead->next = Node1,所以我们就有pNode->next = pHead->next;
第三,直接有pHead->next = pNode;
第四,Node1的prev要指向pNode,即Node1->prev = pNode;但是我们 不知道Node1,可是我们有pHead->next = Node1;所以我们有pHead->next->prev = pNode;
这个看似很复杂,我们要如何记住他,不需要每次都画一个链表图呢。这里我自己总结了一个方法,虽然不确定是否真的正确,但是就目前来看暂时没有什么大问题。对比下面正确和错误的方法我自己的总结是,pHead->next如果做了左值,那么pHead->next就不能在后面作为右值了。
//方法一
pHead->next->prev = pNode;
pNode->next = pHead->next;
pHead->next = pNode;
pNode->prev = pHead;
//方法二
pNode->prev = pHead;
pNode->next = pHead->next;
pHead->next = pNode;
pHead->next->prev = pNode;
//错误的方法,此方法会进入死循环
pHead->next = pNode; //pHead->next作为左值,后面不能再做右值了
pNode->next = pHead->next; //这里pHead->next做右值,出错了
pHead->next->prev = pNode;
pNode->prev = pHead;
//错误的方法二,此方法会进入死循环
pHead->next = pNode; //pHead->next作为左值,后面不能再做右值了
pNode->prev = pHead;
pHead->next->prev = pNode;
pNode->next = pHead->next; //这里pHead->next做右值,出错了
下面给出具体的实现过程,我把错误的方法也一起放上去可以测试一下。
list *Add_List_Node_Head(list *pHead, list *pNode)
{
if (pHead == NULL || pNode == NULL)
{
printf("pHead is null or pNode is null\n");
return NULL;
}
pHead->next->prev = pNode;
pNode->next = pHead->next;
pHead->next = pNode;
pNode->prev = pHead;
/*
//这个也可以正常使用
pNode->prev = pHead;
pNode->next = pHead->next;
pHead->next = pNode;
pHead->next->prev = pNode;
//这个也是可以正常使用,后面两句的顺序跟前面的不一样
pNode->prev = pHead;
pNode->next = pHead->next;
pHead->next->prev = pNode;
pHead->next = pNode;
*/
/*
//这个会进入死循环
pHead->next = pNode;
pNode->next = pHead->next;
pHead->next->prev = pNode;
pNode->prev = pHead;
//这个会进入死循环
pHead->next = pNode;
pNode->prev = pHead;
pHead->next->prev = pNode;
pNode->next = pHead->next;
*/
return pHead;
}
3、查找链表
查找链表还是需要考虑边界条件
list *Search_List_Node(list *pHead,int data)
{
if (pHead == NULL)
{
printf("pHead is null\n");
return NULL;
}
while (pHead->next != NULL)
{
if (pHead->data == data)
{
printf("find node\n");
return pHead;
}
pHead = pHead->next;
}
if (pHead->data == data) //最后一个节点
{
printf("last node\n");
return pHead;
}
else
{
printf("llllll\n");
return NULL;
}
return pHead;
}
4、删除节点
void Delete_List_Node(list *pHead, list *pNode)
{
if (pHead == NULL || pNode == NULL)
{
printf("kkkkkkk\n");
printf("pHead is null or pNode is null\n");
return;
}
while (pHead->next != NULL)
{
if (pHead->data == pNode->data) //双向链表头结点是有数据的,要检测头结点
{
//这里pHead就是要删除的节点
printf("delet node\n");
pNode->next->prev = pNode->prev;
pNode->prev->next = pNode->next;
}
pHead = pHead->next;
}
if (pHead->data == pNode->data) //如果删掉最后一个节点
{
printf("Delete last node\n");
//pNode->next->prev = pNode->prev; //如果删掉最后一个节点不需要前驱了
pNode->prev->next = NULL;
}
return;
}
剩下的就不在 放在上了,main函数的写法跟之前单链表一样,可以参考自己实现一下。
这边博客可能还是会有问题,如果大家发现什么问题,提出来大家一起学习
1333

被折叠的 条评论
为什么被折叠?



