首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

数据结构入门(3)2.链表接口实现

,用动态申请的方式更加方便,插入时只需要将前一个结点里的指针指向自己即可,但新结点刚创建时,里面的指针指向,不要变为野指针。...原理:当头结点时,和尾一样,要讲的是不为的情况: 创建好新结点后,将新结点的下一个指针指向头结点指向的结点,然后头结点指向新结点。...1.当链表有两个以上的结点时,遍历到末尾结点的前驱结点,然后释放掉下一个指针指向的结点,再置即可,如果遍历到删除结点的话,你释放时就会丢失掉前一个结点的地址,那个结点的指针就会变成野指针。...2.当链表或是只有一个结点时,直接释放即可,当然,为什么头结点时也能进行释放?不会构成对空指针的引用吗?...我们来看看Cplusplus上面的描述: 最后一句:如果指针,该功能不做任何改变(因为释放完后还是,人为操作)。 根据free的特性,可以将和单个结点的情况考虑进去。

9810

【数据结构】C语言实现单链表万字详解(附完整运行代码)

的逻辑: 先判断单链表是不是链表, 如果是,直接让头指针指向newnode即可. 如果不是,则需要先找尾,再将newnode的地址链接到尾结点的指针域....入时我们会遇到两种情况: 一种是pos指针指向首结点,这种情况函数的插入逻辑是和头相同的,因此我们可以直接调用头函数来实现该操作....在这种情况,如果直接执行 (*pphead)->next,会出现指针访问,导致程序崩溃。...其中情况三是我们需要特别注意的,找到尾后,我们要使用一个指针记录下尾结点的前一个结点的地址,因为释放尾结点后,我们还需要将它的前一个结点的指针域置....我们使用free()函数将前面开辟的结点的内存逐一释放,释放完将头指针即可.

12110
您找到你想要的搜索结果了吗?
是的
没有找到

单链表(无头单项非循环)

当cue不为指针时,继续访问下一个节点,操作为:cur=cur->next 当cur时,已经访问结束,无需继续遍历。...==NULL表示是一个链表 遍历、尾、头时允许链表存在,头删、尾删中不允许出现 链表不存在: ppead==NULL,表示链表不存在,这种情况不允许存在的,因此,每次都需检查一链表存不存在...tous需要考虑链表是否,如果是链表就不能操作了,因此需要先断言。删除头节点的时候,需要先保存一头节点,否则释放了头节点,就找不到原来的头节点了。...需要借助一个节点,保存待删节点,然后去释放借助的这一个节点并置指针即可。...(遍历、尾、头时允许链表存在),头节点是否存在?什么时候传二级指针

7510

【C语言】探索数据结构:单链表和双链表

以单链表例: 可以看出: 1.链式结构逻辑上是连续的,但是物理上不一定连续 2.现实中的节点一般都是从堆上申请出来的 3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,...struct SListNode { SLDataType data;//要保存的数据 struct SListNode* next; }SLNode; 单链表的尾部插入 这里需要注意的是,插入时可能头节点...); //约定链表不能为,pos也不能为 assert(pos); assert(*pphead); SLNode* node = SLBuyNode(x); //pos是头节点,头...,所以尾实际上就是头节点的左边插入,下面写了两种插入方法 // 双向链表尾 void ListPushBack(ListNode* pHead, LTDataType x) { assert...头哨兵位节点和它的下一个节点之间插入 // 双向链表头 void ListPushFront(ListNode* pHead, LTDataType x) { assert(pHead)

8310

【数据结构】C语言实现双链表的基本操作

单链表中有一点我们没有提到,就是我们通过malloc和calloc申请空间后一定要及时的对接收空间的指针进行检测,看是否指针。...; 进行插入操作时,前驱结点的后继指针执行新结点的操作最好放在最后一步执行; 下面我们来看一双链表的后操作: //双链表的后操作 bool InsertNextDNode(DNode* p, ElemType...p->next = s;//结点p的后继指针指向新结点 } return true;//完成后操作后返回true } 双链表中我们要执行后操作,我们也需要注意几点: 要判断当前结点的后继结点是否指针...,从而选择插入操作的执行步骤: 当前结点的后继结点不为指针时,需要将后继结点的前驱指针的指向对象换成新结点; 当前结点的后继结点指针时,只需要将新结点的后继指针指向指针就行 不管当前结点的后继结点是否指针...free(DNode);//释放当前结点的内存空间 如果是删除的结点表尾结点,则我们只需要将表尾结点的前驱结点指向指针,然后直接释放表尾结点的空间就行,转换成C语言则是如下所示: //删除表尾结点

14710

DS:单链表实现队列

3、不需要使用二级指针了       以往我们单链表的实现中,使用的是二级指针,因为单链表中的phead就是结构体指针类型,而单链表的头删以及头都需要改变phead,所以我们需要传的是该结构体指针的地址...必须不能是NULL,因此要分开讨论ptailNULL的情况 2.3.3 为什么assert(pq->phead == NULL)      因为我们考虑ptail的时候,不能用成员next,但是因为按道理来说一般情况...ptail成为一个野指针,所以我们需要考虑只有一个节点多个节点的情况 if (pq->phead->next == NULL)//一个节点的情况,直接将这个节点释放并置即可 { free(pq...2.9 销毁队列 void QueueDestory(Queue* pq) { assert(pq);//判断传的是不是指针 //要逐个节点释放 QNode* pcur = pq->phead;...ptail成为一个野指针,所以我们需要考虑只有一个节点多个节点的情况 if (pq->phead->next == NULL)//一个节点的情况,直接将这个节点释放并置即可 { free(pq

10710

YYCache 源码剖析:一览亮点

这里使用 __unsafe_unretained 而不使用 __weak 笔者有些疑惑,虽然两者都不会持有指针所指向的对象,但是指向对象释放时,前者并不会置指针,形成野指针。...释放部分: 这里作者使用了一个容器将要释放的结点装起来,然后某个队列(默认是非主队列)里面调用了一该容器的方法。...20K 时,数据越小 SQLite 读取性能越高;单条数据大于 20K 时,直接写文件速度会更快一些。...(更详细的说明看文末链接) 所以作者对磁盘缓存的处理方式 SQLite 结合文件存储的方式。...(1)磁盘缓存的文件结构 首先,需要了解一作者设计的磁盘中的文件结构(YYKVStorage.m中作者的注释): /* File: /path/ /manifest.sqlite

1.4K61

开卷数据结构?!单链表实现超详解~

("NULL\n"); return; } 链表尾数据 注意: 对于不带头链表,尾数据也可能是插在链表首元素的地址(当链表),需要修改链表指针的内容(故需要传入链表指针的地址) 插入数据要开辟节点...要尾数据则需要遍历找到链表的尾节点 尾则是将前一个节点的址域该成该节点地址(链表则是将链表指针内容该成该节点地址) 参考代码: //链表尾数据 void SListPushBack(SLTNode...但是对于非正常传入链表指针(不是链表指针的地址)且此时链表指针则会发生报错(assert报错会告诉错误位置),告诉程序员应该传入链表指针的地址 注:这是一个非常好的代码习惯 图示: 链表前删数据...注意: 对于动态开辟的内存空间,使用后一定要记得的进行释放(避免造成内存泄漏) 因为链表节点是一个个开辟的,同样的释放也需要一个个进行释放 循环遍历释放当前节点前需保存后一个节点的地址,避免地址丢失无法释放...释放完后,还需将链表指针给置(避免使用野指针) 参考代码: //链表节点释放 void SListDestory(SLTNode** pphead) { //避免传入错误(直接报错便于找到错误位置

23940

【线性表】—动态顺序表的增删查改实现

不过这里有一点需要注意的是,任意位置的插入与删除,如果这个位置是下标0的地方,这就等同于头插头删,如果这个pos是在下标size处,这就是尾尾删 所以我们的头可以也写: //头 void...销毁 最后是顺序表的销毁,也很简单,释放a指向的空间,并置a指针,然后size与capacity归零即可 这里注意,假如a是个指针(未开辟空间就直接释放),就不能进行释放,具体原因动态内存章节已讲解...,即指针不能释放,所以加了个if判断条件。...free(ps->a); ps->a = NULL; ps->capacity = 0; ps->size = 0; } } 总结 最后再总结以下,需要注意的细节地方无非是传来的参数是否指针...,涉及到任意位置就要考虑pos是否位置合理,还有就是只要涉及插入数据的操作,就必然要考虑到扩容,涉及到删除的操作,就必然考虑到表问题。

43640

【数据结构】C语言实现顺序表万字详解(附完整运行代码)

首先在进入初始化程序后,我们应当对函数传进来的参数做一个检验,即检验ps指针是否指针,如果该指针的话,那么指针变量就没有指向任何有效的内存地址,即指针变量的值0或NULL。...如果我们用指针来接收malloc函数返回的指针,那么就相当于没有为分配的内存分配任何指针变量,这意味着我们无法访问该内存块,也无法释放该内存块,因为我们没有指向它的指针。...spm=1001.2014.3001.5502 需要注意的是,这里我们对传入的ps指针的断言需要与后面我们要实现的链表中的断言作一区分:顺序表中要求ps不能为,是因为一旦ps,那么传入的指针一定是一个非法的指针...尾删的逻辑同样很简单,不需要挪动元素,只需要在删除前检查顺序表是否表就行,然后将size--一.(如果,则不需要删除,直接返回即可)....我们使用free()函数释放掉之前动态开辟的数组arr,然后将arr置指针,最后将size,capacity的值置0即可.

22010

浅谈list与vector的区别

我们此时跳出来看另一个问题,穿越回到古代,你选择诸葛亮当你的军师还是选项羽挂帅征战,其实这也是上文的相同问题,这两个容器看起来效果一样但是不同的情况,这2个容器的有不同的优势,甚至某些情况它们是不可替代的...vector list 底 层 结 构 动态顺序表,一段连续空间 带头结点的双向循环链表 随 机 访 问 支持随机访问(下标访问),访问某个元素效率O(1) 不支持随机访问,访问某个元素 效率O(N) ...入 和 删 除 任意位置插入和删除效率低,需要搬移元素,时间复杂度O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 任意位置插入和删除效率高,不 需要搬移元素(直接添节点...),时间复杂度O(1) 间 利 用 率 底层连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低 迭 代 器 原生态指针...对原生态指针(节点指针)进行封装 迭 代 器 失 效 插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 插入元素不会导致迭代器失效

25720

数据结构 | 单链表

---- 正文 链表打印与销毁 打印 单链表 创建时是一个结构体类型的指针,一开始指向,只有经过插入数据后才会有自己的指向 ,因此我们可以根据这一特点,遍历 整个 单链表 ,并输出其中的 数据域 data...单链表 尾是比较费劲的,因为得先通过头节点指针向后 遍历 找到尾节点,然后将尾节点与新节点之间建立链接关系,其中还得分情况尾 链表,直接把新节点赋给头节点 不为,就需要找到尾节点,建立链接关系...} 尾删 尾删操作与尾基本一致,同样是需要找到尾节点,不过每次 tail 指针向后移动前,会先使用一个 prev 指针保存 tail 的信息,当 tail 尾节点时,释放 tail ,并将 prev...= node) { prev = cur; //要找到目标节点的上一个节点 cur = cur->next; } //判断一,是否表插入 //走的是尾的那一套思想 if (...= node) { prev = cur; //要找到目标节点的上一个节点 cur = cur->next; } //判断一,是否表插入 //走的是尾的那一套思想 if (

10820

DS:单链表的实现

4.2 尾 一般情况,当链表不为,找到最后一个结点ptail,让最后一个结点的next指针指向新的结点。...4.3 头 一般情况,我们只需要让newnode的next指针指向原来的头节点,再让newnode成为新的头节点,当链表时也是人newnode新的头节点,所以不需要分开讨论 void...4.5 尾删 在一般情况,我们需要找到尾节点的前一个结点prev,让他的next指针指向NULL,然后再释放尾结点并置,因为我们需要找尾结点的前一个结点,如果该链表恰好只有一个结点时,是没有前结点的...4.6 头删 一般情况,令*pphead指向第二个结点,然后释放掉第一个结点即可,链表只有一个结点的情况,也是释放掉第一个结点,所以不需要分开讨论,链表时头删没有意义!!...4.10 删除指定位置结点 一般情况,要找到pos的前一个结点prev,然后让他的next指针指向pos->next,然后再释放pos并置,因为需要找pos的前一个元素,所以还需要考虑

9110

顺序表:数据结构的建筑积木

线性表的元素个数n(n≥0)定义线性表的长度,当n=0时,称为表。 线性表逻辑上是线性结构,也就说是连续的一条直线。...修改如下: void SLInit(SL*ps) { ps->array = NULL; ps->size = 0; ps->capacity = 0; } 这里若ps指针...多数情况,顺序表的实现不会立即释放每次删除操作后的内存空间,因为频繁的内存分配和释放操作会影响性能。相反,它保留这些空间以支持未来的添加操作,从而提高了整体的内存使用和管理效率。...位置插入x void SLErase(SL* ps, int pos);//顺序表删除pos位置的值 关于数据插入,首先判断三件事: ps是否指针 pos是否规定位置 空间是否足够 void SLinsert...指定位置删除 首先进行判断是否指针和指定位置,再进行删除,代码如下 void SLErase(SL* ps, int pos) { assert(ps !

8510

无头单向非循环链表(C语言实现)

单链表 设计思路 实现增删查改的准备工作 头 头删尾删 查找与销毁 pos之后插入数据x的结点与删除pos后面的结点 完整代码 设计思路 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表...}ct; 定义一个头节点 //test.c ct* head = NULL;//头结点指针 默认指向,如果没有数据就为 开辟结点空间 //linked.c ct* crunode(type...头节点指针 { *phead = newnode;//让头节点指向新创建的结点 } else//头节点指针不为 { ct* cur = *phead; while (cur->...>next = *phead; *phead = newnode; } 头不需要分情况,因为就算链表里面,头是将头节点指向的位置储存到新创建结点的next中。...cur->next指向的是最后一个结点,释放掉 cur->next = NULL;//将末尾节点的next置指针 } } 头删 void SListPopFront(ct** phead

35800

单链表的(增删查改)的实现

2.链表优点 1.空间上按需所给空间 2.头部和中间插入时,不需要挪动数据 二、单链表的实现 1.函数的定义和结构体的创建——list.h #include #include<stdlib.h...此时发现传过去的&pehad,接收是二级指针,传址调用才能真正改变主函数中 phead 指针 但比如 打印或者查找位置并没有改变phead指针,所以也就不用传地址了 4.单链表的接口实现 1.尾...NULL) { newnode->data = x; newnode->next = NULL; } if (*pphead == NULL)//时...>next = newnode; } } 2.尾删 void stackpopback(slistnode** pphead)//尾删 { if (*pphead == NULL)//...要把尾节点free 尾节点的前一个节点的指针NULL { slistnode* prev = NULL;//prev指向前一个节点 slistnode*

53030

【数据结构】—带头双向循环链表的实现(完美链表)

,但是我们发现该链表实现尾与尾删时比较麻烦,要先从头节点进行遍历,找到尾节点,时间复杂度O(N),而本次所讲的带头双向循环单链表,则可以直接找到尾节点。...如下: 这里的尾也满足表情况进行尾。...如下: 这里要注意的就是表情况是不可以继续删除的。...ListPrint(phead);// 5 4 3 0 1 //链表销毁 ListDestory(phead); } 总结 该链表用起来真的很爽,能直接找到头尾节点,并且因为有头的存在,就不需要考虑是否表的情况的插入...真的是链表中的完美存在,不过进行删除操作时,一定要考虑表情况下不可进行删除。因此要加个assert进行断言。

49720

【线性表】—不带头单向非循环链表的增删查改

(避免野指针) return newnode;//返回新节点 } 尾与尾删 尾 还是需要进行画图,这样才能更好的理解 但是这里假如传来的是个指针,即假如是一个的链表,那尾时这个新节点就作为头节点来使用...而进行修改后(表情况进行尾),后面的再次尾其实改变的就不是该变量了,而是该变量的结构体成员next,以及next节点指向的data 尾删 画图解决一切。...这里需要注意的就是,假如只有一个节点的情况,该节点的next就是指针,然后再next就形成了指针的解引用操作(NULL->next)这是错误的,所以我们要考虑到只剩一个节点的特殊情况,另外,还要注意表状态是不可删除的...这里只需要将新节点的next指向目前的头指针,然后头指针再更新新节点即可。...} 总结 在这里,一定要多画图,根据图形来理清思路,然后再进行写代码,同时一定要考虑考虑特殊情况,比如表状态能不能删除,比如free的时候会不会存在野指针, 并且还建议大家边写边调试,不要一口气从尾写完

33120

【线性表】—带头哨兵卫单链表的应用

目录 前言 实战练习 链表分割 合并两个有序链表 总结 前言 我们之前学过了无头单向非循环链表的实现,但是我们发现,该链表的时候有一点不好,就是第一次尾时,会改变头节点,所以我们在上篇文章实现时传的是二级指针...而本次所讲哨兵卫单链表时则不用改变头节点。所谓哨兵卫,其实就是带了一个头节点,该节点不作为用来存储数据。如下: 接下来我们通过具体题目来感受该结构带来的好处。...不过这里我们用哨兵卫单链表实现的话,就不需要考虑到链表是否的情况。...; free(newnode2); return phead; } }; 这里我们就不需要考虑链表是否的情况,会省很多事,因此对于一些单链表的题目,只要涉及到尾问题...这里涉及到尾,为了不用考虑表情况的尾,我们依然采用哨兵卫单链表。

17950
领券