前言
提示:本文旨在为正在学习数据结构或已经学习过数据结构的朋友复习单向链表的知识,如果说数据结构的难度天花板是10分,那么本文所阐述的单向链表难度在2~3分,而且链表的熟悉程度很大部分程度决定了后续数据结构的顺利程度,希望读者朋友们能完全掌握!
正文开篇
一、链表是什么?
链表的作用
单向链表是一种数据结构,由一系列节点组成,每个节点包含了一个数据元素和一个指向下一个节点的指针。通过这种方式,节点被链接在一起形成链表。链表通常被用来实现没有固定大小的数据集合,可以动态地添加或移除元素。单向链表只能在一个方向上遍历,即从头节点开始一直遍历到尾节点,但不能反向遍历。
链表的结构
链表由一系列节点组成,第一个节点称为头节点,最后一个节点称为尾节点,尾节点的指针为空。每个节点只包含指向下一个节点的指针,因此称为单向链表。单向链表可以插入、删除数据,但是查找节点效率较低。
二、链表基础操作
1.创建节点(BuyLTnode)
- 创建一个节点,返回newnode,newnode是一个结构体指针
- newnode->data可以找到数据域
- newnode->next可以找到指针域,即下一个节点的地址
图解:
代码实现
SLTNode* BuyLTNode(SLTDataType x)//按需创建一个新结点
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode==NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
2.打印数据(SLTPrint)
顾名思义,打印数据即将每个结点的数据域都找到并打印下来
效果:
将phead复制一份,目的是拿备份项cur去遍历从而达到不修改头指针的目的
当cur解引用为空指针说明找到尾节点,遍历结束。
代码实现:
void SLTPrint(SLTNode* phead)//phead存放头结点地址
{
SLTNode* cur = phead;
while (cur!=NULL)
{
printf("[%d]-> ", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
3.尾部插入(SLTPushBack)
尾插需要分两种情况:
a.无节点时
创建一个节点,此时头指针即为newnode
b.有节点时
1.创建一个节点
2.查找尾节点
3.尾节点和新节点连接
图解:
1.SLTNode* newnode = BuyLTNode(x);
2.复制一份头指针,拿局部变量tail去遍历
SLTNode* tail = *pphead;
找尾节点
while (tail->next != NULL)
{
tail = tail->next;
}
将尾节点的指针域改成newnode,找到新节点
c.代码实现
void SLPushBack(SLTNode** pphead, SLTDataType x)//要改变plist,传plist的地址,故使用二级指针接收
{
SLTNode* newnode = BuyLTNode(x);
if (*pphead==NULL)
{
*pphead = newnode;//改变结构体指针plist
}
else
{
SLTNode* tail = *pphead;//前提是plist不能为空,故分两种情况讨论
while (tail->next != NULL)
{
tail = tail->next;
}
//tail去找尾结点
/*SLTNode** newnode = BuyLTNode(x);*/
tail->next = newnode;//链接的关键
}
}
4.头部插入(SLTPushFront)
a.创建新节点
b.新节点与头节点连接
即将新节点中的指针改为头指针
c.更新头指针
把新节点的地址赋给头指针
完成
d.代码实现
void SLPushFront(SLTNode** pphead, SLTDataType x)//让newnode指向原来的头节点
{
SLTNode* newnode = BuyLTNode(x);
newnode->next = *pphead;//起到新结点与原头结点链接的作用
*pphead = newnode;//头指针的更新
}
5.尾部删除(SLTPopBack)
即把最后一个节点抹杀
a.复制头指针
b.寻找倒数第二个节点
这里运用了一个巧妙的方法,两次解引用寻空指针
c.毁灭吧,尾节点
目的是防止内存泄露,同时修改指针域
d.代码实现
void SLPopBack(SLTNode** pphead)
{
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
6.头部删除(SLTPopFront)
a.复制头指针
b.更新头指针
c.毁灭吧,头节点
d.代码实现
void SLPopFront(SLTNode** pphead)
{
SLTNode* cur = *pphead;
*pphead = cur->next;
free(cur);
cur= NULL;
}
7.常见问题(Q&&A)
Q:
为啥有的时候用二级指针接收&plist,比如SLPushFront,而SLTPrint却用一级指针捏
A:
因为要想改变某个参数,必须要传该传该参数的地址
要改变结构体,用结构体指针接收即可
要改变结构体指针plist,需要用指向结构体指针的指针结束,即二级指针
三.测试用例
1.尾插
依次尾插数据12345
再插入数字7
再插入8,9
2.尾删
删除数据9
3.头删
删除数据5
4.头插
头插数据666
四.总结
本人觉得对于链表的基础操作解释还算详尽,希望读者朋友们呢耐心读完,一定收获不小。
五.番外(松下DMC-GX1GK)
贪玩的笔者又秉承着三分热血玩起了摄影