顺序表缺点:
例如:当前容量为200,如果有201个待插入数据,势必要增容到400(原来容量的两倍),这样就浪费了199个空间.
我们不妨来看一下链表的存储结构.
链表的概念:
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.也是属于线性表的一种.
栗子
单链表的结点声明:
typedef int DateType; typedef struct SListN0de { DateType Date;//数据域 struct SListN0de* next;//指针域 }SLTNode;
结点:
通过上图我们不难知道:
共有八种链表,我们主要学习不带头单向链表与带头双向链表,学会这两种,其它的大同小异,写起来并不苦难.
单向、双向:
不带头、带头:
带头与不带头的区别在于:
带头:链表是通过一个特殊的结点—头结点指向链表的第一个有效结点.
不带头:通过结点指针指向链表的第一个有效结点.
头结点作用:传送门
不须换、循环:
重点掌握:
typedef int DateType; typedef struct SListN0de { DateType Date;//数据域 struct SListN0de* next;//指针域 }SLTNode;
单链表初始化:
单链表是不需要初始化操作的,只需要创建一个结点指针就行.初始状态:指针指向NULL.
头指针:
SLTNode* plist=NULL;
我们需要插入数据时,只需要申请一个结点,将数据放入结点,然后将结点链接起来就行.
单链表的"尾插":
单链表的尾插步骤:
由于单链表的结点之间不一定是连续存储的,不支持向顺序表那样的随机访问,需要通过遍历才能找到目标结点.
图解:
那如果链表本身就是空链表呢?
此时需要修改头指针,将头指针指向这个新节点.
注意:
这点很重要,因为需要修改头指针,而头指针的类型是:SLTNode*,相信友友们学到这里应该知道,如果想要在函数中形参的改变影响实参,则需要传址调用,通过地址来影响实参.
那么,指针的地址是?二级指针相信友友们应该没有忘记.
"尾插"函数声明:
void PushBack(SLTNode** pphead, DateType x)
pphead需要断言:
pphead是指向 *pphead(一级指针/头指针)的指针,即值存储的是头指针的地址,只要头指针存在,则不为空.而头指针一定存在.
*phead不能断言:
*phead是头指针,头指针在链表为空时,头指针的值是NULL,所以不能断言.
链表中有数据时,指向第一个结点,值是第一个结点的地址.
代码:
void PushBack(SLTNode** pphead, DateType x) { assert(pphead);//如果头指针的地址为NULL,则报错. SLTNode*tail = *pphead;//创建一个指针代替头指针遍历 SLTNode* newnode = BuyNode(x); //*pphead代表代表头指针,phead表示头指针的地址 //如果*pphead指向NULL,说明为空链表 if (*pphead == NULL) { //这里可以使用tail代替*phead吗? //不能,因为这里要改变的是,头结点的指针,需要用二级指针(解引用)来改变 *pphead = newnode;//空链表是将头指针指向新节点 } else { //找尾巴,画图解析 //这里可以使用tail,是因为,要改变的是结构体的内容,只需要用结构体指针(解引用)就行 while ( tail->next != NULL)//如果该节点的下一个节点是空指针则停止循环 { tail = tail->next; } tail->next = newnode;//让尾节点的next指向新节点. } }
尾插是不是显得有些麻烦?那我们试着看一下头插.
头插步骤:
图解:
代码实现:
//写法1 void PushFront(SLTNode** pphead, DateType x) { assert(pphead); SLTNode* newnode = BuyNode(x); //下面两句的顺序不能变 newnode->next = *pphead; *pphead = newnode; }
写法2:
//写法2 void PushFront(SLTNode** pphead, DateType x) { assert(pphead); SLTNode* newnode = BuyNode(x); SLTNode* phead = *pphead;//创建一个临时指针,保存一下头指针指向的头结点. //顺序随便改 *pphead = newnode; newnode->next = phead; }
两种方法都比较好理解,也很简单,单链表的头插效率很高,不需要遍历,
该函数很简单,只需要通过查找目标结点函数找到目标结点的位置,然后将将新节点链接上去就行了.
步骤:
图解:
//使用此函数之前可以先使用,查找目标结点函数(SListFind),找到位置先 void SLTInsertBack( SLTNode* pos, DateType x) { assert(pos); SLTNode* newnode = BuyNode(x); newnode->next = pos->next; pos->next = newnode; }
Copyright © 2023 leiyu.cn. All Rights Reserved. 磊宇云计算 版权所有 许可证编号:B1-20233142/B2-20230630 山东磊宇云计算有限公司 鲁ICP备2020045424号
磊宇云计算致力于以最 “绿色节能” 的方式,让每一位上云的客户成为全球绿色节能和降低碳排放的贡献者