数据结构体进阶链表【带头双向循环链表,单向链表的优化,从根部解决了顺序表的缺点】一文带你深入理解链表_求单向链表长度的算法怎么优化

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

}


#### 🤎2.5链表实现后的销毁:



void ListDestroy(LTNodephead)
{
LTNode
cur=phead->next; //定义一个结构体指针进行遍历销毁
while(cur!=phead)
{
LTNode*next=cur->next;
free(cur);
cur=next;
}
free(phead); //最后释放头节点
}


## 3、链表功能的实现


#### ❤️3.1链表的头插:



void ListPushFront(LTNodephead,LTDataType x)
{
assert(phead);
LTNode
newnode=BuyListNode(x); //先定义一个节点进行头插
newnode->next=phead->next; //这里要考虑顺序问题,不然很容易出错,先让定义的next指向哨兵的下一个,然后哨兵下一个的prev指向我们新的节点
//然后让我们我们哨兵的next指向新节点,新节点的prev指向哨兵, 这里如果不理解的可以画一下图
//或者我么先定义一个节点把哨兵的下一个进行保存这里就不用考虑先后顺序了,大家可以试一试
phead->next->prev=newnode;
phead->next=newnode;
newnode->prev=phead;
}


#### ❤️3.2链表的尾插:



void ListPushBack(LTNodephead,LTDataType x)
{
assert(phead);
LTNode
newnode=BuyListNode(x); //定义一个新节点
LTNode* tail=phead->prev; //找到尾节点
tail->next=newnode; //然后就是交换结构体里面的next prev指向的位置来实现插入。头插我已经讲述过程了,主要是大家尝试一下画画图
newnode->prev=tail;
newnode->next=phead;
phead->prev=newnode;
}


#### ❤️3.3链表的头删:



void ListPopFront(LTNodephead)
{
assert(phead);
assert(!ListEmpty(phead)); //这里防止只剩下一个哨兵,如果只剩下一个哨兵还进行删除就强制类型报错
LTNode
first=phead->next; //定义一个first指向哨兵的next
LTNode* second=first->next; //在定义一个second指向first的next
phead->next=second;
second->prev=phead;
free(first); //把first删除后进行free
first=NULL; //first指针指向空,防止出现野指针
}


#### ❤️3.4链表的尾删:



void ListPopBack(LTNodephead)
{
assert(phead);
assert(!ListEmpty(phead));//这里防止只剩下一个哨兵,如果只剩下一个哨兵还进行删除就强制类型报错
LTNode
tail=phead->prev;
tail->prev->next=phead;
phead->prev=tail->prev;
free(tail);
tail=NULL;
}


#### ❤️3.5链表的pos位插入:



void ListInsert(LTNodepos,LTDataType x)
{
assert(pos); //保证传入的pos位不是空指针
LTNode
prev=pos->prev; //找到pos位的前一位
LTNode*newnode=BuyListNode(x); //定义一个新节点进行插入
pos->prev=newnode;
newnode->next=pos;
prev->next=newnode;
newnode->prev=prev;
}


#### ❤️3.6链表的pos位删除:



void ListErase(LTNodepos)
{
LTNode
prev=pos->prev;//找到pos位的前一个
LTNode*next=pos->next; //找到pos的后一个
prev->next=next;
next->prev=prev;
free(pos); //释放pos
}


#### ❤️3.7链表的查找:



LTNode* ListNodeFind(LTNodephead,LTDataType x) //查找后返回指针,如果找到返回找的到的结构体指针,没有找到返回空指针
{
assert(phead);
LTNode
cur=phead->next; //定义一个结构体指针进行遍历寻找
while(cur!=phead)
{
if(cur->data==x)
{
return cur; //找到后返回的指针
}
cur=cur->next;
}
return NULL;
}


## 4、带头双向循环链表的源码


#### 💚4.1 List.h



> 
> 
> ```
> #ifndef List_hpp
> #define List_hpp
> 
> #include <stdio.h>
> #include<stdlib.h>
> #include<assert.h>
> #include<stdbool.h>
> typedef int LTDataType;
> typedef struct ListNode
> {
>     struct ListNode*next;
>     struct ListNode*prev;
>     LTDataType data;
> }LTNode;
> LTNode* ListInit();
> LTNode*  BuyListNode(LTDataType x);
> void  ListPushBack(LTNode*phead,LTDataType x);
> void ListNodePrint(LTNode* phead);
> void  ListPushFront(LTNode*phead,LTDataType x);
> void ListPopBack(LTNode*phead);
> bool ListEmpty(LTNode*phead);
> void ListPopFront(LTNode*phead);
> size_t ListSize(LTNode*phead);
> LTNode* ListNodeFind(LTNode*phead,LTDataType x);
> void ListInsert(LTNode*pos,LTDataType x);
> void ListErase(LTNode*pos);
> void ListDestroy(LTNode*phead);
> 
> #endif /* List_hpp */
> 
> ```
> 
> 


#### 💚4.2List.c



> 
> 
> ```
> #include "List.hpp"
> LTNode* ListInit()
> {
>     LTNode*guard=(LTNode*)malloc(sizeof(LTNode)); //这里就像定义一个哨兵,也就是头节点,就不用再担心头节点是不是NULL了
>     if(guard==NULL) //确保能够正常扩容
>     {
>         perror("ListNodeInit");
>     }
>     guard->next=guard; //头节点的下一个先指向自己
>     guard->prev=guard; //头节点的上一个也先指向自己
>     return guard;
> }
> LTNode*  BuyListNode(LTDataType x)
> {
>     LTNode* newnode=(LTNode*)malloc(sizeof(ListNode)); //节点申请空间
>     if(newnode==NULL)
>     {
>         perror("BuyListNode");
>     }
>     newnode->data=x;  //节点所带的值
>     newnode->prev=NULL;
>     newnode->next=NULL;
>     return newnode;
> }
> void ListNodePrint(LTNode* phead)
> {
>     assert(phead);   //保证传入的不是空指针
>     LTNode* cur =phead->next; //定一个结构体指针进行遍历打印
>     printf("guard<=>");  //这里是为了打印效果好看一点
>     while(cur!=phead)
>     {
>         printf("%d<=>",cur->data);
>         cur=cur->next;
>     }
>     printf("\n");
> }
> void ListPushBack(LTNode*phead,LTDataType x)
> {
>     assert(phead);
>     LTNode* newnode=BuyListNode(x);  //定义一个新节点
>     LTNode* tail=phead->prev;  //找到尾节点
>     tail->next=newnode;  //然后就是交换结构体里面的next prev指向的位置来实现插入。头插我已经讲述过程了,主要是大家尝试一下画画图
>     newnode->prev=tail;
>     newnode->next=phead;
>     phead->prev=newnode;
> }
> void  ListPushFront(LTNode*phead,LTDataType x)
> {
>     assert(phead);
>     LTNode* newnode=BuyListNode(x); //先定义一个节点进行头插
>     newnode->next=phead->next;  //这里要考虑顺序问题,不然很容易出错,先让定义的next指向哨兵的下一个,然后哨兵下一个的prev指向我们新的节点
>         //然后让我们我们哨兵的next指向新节点,新节点的prev指向哨兵, 这里如果不理解的可以画一下图
>        //或者我么先定义一个节点把哨兵的下一个进行保存这里就不用考虑先后顺序了,大家可以试一试
>     phead->next->prev=newnode;
>     phead->next=newnode;
>     newnode->prev=phead;
> }
> bool ListEmpty(LTNode*phead)
> {
>     assert(phead);
>     return phead->next==phead; //如果头和头的下一个指向同一个则返回真,证明只有一个哨兵
> }
> void ListPopBack(LTNode*phead)
> {
>     assert(phead);
>     assert(!ListEmpty(phead));//这里防止只剩下一个哨兵,如果只剩下一个哨兵还进行删除就强制类型报错
>     LTNode*tail=phead->prev;
>     tail->prev->next=phead;
>     phead->prev=tail->prev;
>     free(tail);
>     tail=NULL;
> }
> void ListPopFront(LTNode*phead)
> {
>     assert(phead);
>     assert(!ListEmpty(phead)); //这里防止只剩下一个哨兵,如果只剩下一个哨兵还进行删除就强制类型报错
>     LTNode*first=phead->next;  //定义一个first指向哨兵的next
>     LTNode* second=first->next; //在定义一个second指向first的next
>     phead->next=second;
>     second->prev=phead;
>     free(first);  //把first删除后进行free
>     first=NULL;  //first指针指向空,防止出现野指针
> }
> size_t ListSize(LTNode*phead)
> {
>     assert(phead);
>     LTNode*cur=phead->next;
>     size_t n=0;
>     while(cur!=phead)
>     {
>         ++n;
>         cur=cur->next;
>     }
>     return n;
> }
> LTNode* ListNodeFind(LTNode*phead,LTDataType x)  //查找后返回指针,如果找到返回找的到的结构体指针,没有找到返回空指针
> {
>     assert(phead);
>     LTNode* cur=phead->next;  //定义一个结构体指针进行遍历寻找
>     while(cur!=phead)
>     {
>         if(cur->data==x)
>         {
>             return cur;  //找到后返回的指针
>         }
>         cur=cur->next;  
>     }
>     return NULL;
> }
> void ListInsert(LTNode*pos,LTDataType x)
> {
>     assert(pos); //保证传入的pos位不是空指针
>     LTNode*prev=pos->prev; //找到pos位的前一位
>     LTNode*newnode=BuyListNode(x);  //定义一个新节点进行插入
>     pos->prev=newnode;
>     newnode->next=pos;
>     prev->next=newnode;
>     newnode->prev=prev;
> }
> void ListErase(LTNode*pos)
> {
>     LTNode*prev=pos->prev;//找到pos位的前一个
>     LTNode*next=pos->next; //找到pos的后一个
>     prev->next=next;
>     next->prev=prev;
>     free(pos);  //释放pos
> }
> void ListDestroy(LTNode*phead)
> {
>     LTNode*cur=phead->next; //定义一个结构体指针进行遍历销毁
>     while(cur!=phead)
>     {
>         LTNode*next=cur->next;
>         free(cur);
>         cur=next;
>     }
>     free(phead); //最后释放头节点
> }
> 
> ```
> 
> 


#### 💚4.3Test.c



> 
> 
> ```
> #include "List.hpp"
> void test1()
> {
>     LTNode* plist=ListInit();
>     ListPushBack(plist,1);
>     ListPushBack(plist,2);
>     ListPushBack(plist,3);
>     ListPushBack(plist,4);
>     ListPushBack(plist,5);
>     ListPopBack(plist);
>     ListPopBack(plist);
>     ListNodePrint(plist);
>     ListDestroy(plist);
>     plist=NULL;
> }
> void test2()
> {
>     LTNode* plist=ListInit();
>     ListPushFront(plist,1);
>     ListPushFront(plist,2);
>     ListPushFront(plist,3);
>     ListPushFront(plist,4);
>     ListPushFront(plist,5);
>     ListPopFront(plist);
>     ListPopFront(plist);
>     size_t ret=ListSize(plist);
>     printf("%zu\n",ret);
>     ListNodePrint(plist);


![img](https://img-blog.csdnimg.cn/img_convert/5beb95ea0dd0674e1e7815895a51b3f4.png)
![img](https://img-blog.csdnimg.cn/img_convert/0d0f26a9040b5953f3bb56cba7ffc096.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

rintf("%zu\n",ret);
>     ListNodePrint(plist);


[外链图片转存中...(img-4yjGqrt8-1715464791055)]
[外链图片转存中...(img-VcIco8EI-1715464791055)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值