链表实现与总结

目录

一.什么是链表

二.链表的类型

1.单向或双向

 2.带头或不带头

3.循环或非循环

 三.单链表实现

1.不带头结点的单链表

1.BuySLTNode函数(创建一个新结点)

 2.CreateSList(创建单链表)

 3.SLTPrint(打印单链表)

4.SLTPushback(尾插)

5.SLTPopback(尾删)

6.SLTPushFront(头插)

7.SLTPopFront(头删)

8.SLTFind(查找链表中元素x的位置)

9.SLTInsertAfter(指定pos位置后插入)

10.SLTInsert(指定pos位置前插入)

11.SLTEraseAfter(删除pos位置后的结点)

12.SLTErase(删除pos位置结点)

13.SLTDestroy(清空链表)

2.带头结点的单链表

1.CreateLinklist(创建单链表)

2.ShowLinklist(打印单链表)

3.SearchLinklist(给定pos下标,查找链表)

4.InsertLinklist(插入元素进入链表)

5.DeleteLinklist(删除下表为i的后一个结点)

四.带头双向循环链表实现

1.BuyListNode(创建新结点)

 2.LTInit(初始化链表)

 3.LTPrint(打印链表)

4.LTPushBack(尾插)

5.LTPopBack(尾删)

6.LTPushFront(头插)

7.LTPopFront(头删)

8.LTFind(找指定元素的指针)

9.LTInsert(在pos位置前插入新结点)

10.LTErase(删除pos位置结点)

11.LTEmpty(判断链表是否为空)

12.LTSize(返回链表长度)

13.LTDestroy(摧毁链表)

五.顺序表与链表的对比


一.什么是链表

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的.

一般链表每个结点可以划分为数据域data(用来存储数据),指针域ptr(用来连接每个结点)两个部分.

 就像侦探破案一样,进入一个房间后,可以得到下一个新的空间的地址,从而找寻到新的空间.

(PS:两个房间可能紧挨着,也可能分离).

可以说指针是实现链表最核心的部分.

二.链表的类型

1.单向或双向

 2.带头或不带头

 在链表最前面设置一个哨兵结点,里面不存放任何数据,称为带头链表,反之,称为不带头链表.

3.循环或非循环

 三.单链表实现

在实现上来说,带头结点的单链表会比不带头结点的单链表要轻松,因为头结点可以把增删的代码

进行统一,而不带头结点的链表则不能.这也是带头结点的链表的一大优势.在后面的实现中我们也

能体会到.

但oj题中大多是不带头结点的单链表,并且在后面哈希桶、图的邻接表中,也并没有带头结点,所

以,不带头结点的单链表也有其实际的意义,需要优先进行实现.

下面采取的形式呢

主要是给出函数实现时所需要思考的地方,而在代码实现中,也会逐一解答这些思考点.

1.不带头结点的单链表

单链表声明:

SlistNode为单Single链表的意思,SLT为其缩写,Node是结点的意思

 关于不带头结点的单链表,大致有以下操作.

在开始之前,我们必须记住一件很重要的事.

形参的改变,不会影响实参.

如果要改变结构体,我们传进函数的是结构体的指针.

如果要改变指针,我们传进函数的就必须是二级指针.

接下来,将会逐一介绍并实现函数.

1.BuySLTNode函数(创建一个新结点)

思考点:1.1结点申请有没有可能失败?假如失败,我们需要进行什么操作?

              1.2返回参数是什么?

 2.CreateSList(创建单链表)

思考点:2.1 现在已经有了创建一个结点的函数,如果将它们连接起来,形成链表呢?

              2.2 假如链表为空,操作和链表中已经有元素操作是一样的吗?

 3.SLTPrint(打印单链表)

思考点: 3.1 如何判断链表不再需要打印?

4.SLTPushback(尾插)

思考点:  4.1  链表为空的时候可以尾插吗?如果可以尾插,该如何实现呢?(函数需要什么参数)

               Tips:考虑二级指针,这样就可以实现修改头指针的操作.

                4.2  如何找到链表尾部呢?

5.SLTPopback(尾删)

思考点:  5.1  链表为空的时候可以尾删吗?如何区分链表是否为空?

                5.2  可以直接释放最后一个结点吗?是否需要考虑修改前面结点next的值?

                5.3  链表只有一个元素和有多个元素,操作是否有什么不同?

6.SLTPushFront(头插)

思考点:6.1  空链表可以头插吗?

              6.2  头指针是否需要修改?如果需要,如何实现呢?

7.SLTPopFront(头删)

思考点:7.1  空链表可以头删吗?

              7.2  头指针是否需要修改?如果需要,如何实现呢?

8.SLTFind(查找链表中元素x的位置)

 思考点:8.1  空链表可以查找吗?

              8.2  可以用cur->next来作为循环条件吗?

9.SLTInsertAfter(指定pos位置后插入)

思考点:9.1  需要判断头指针为空吗?

              9.2  头指针是否需要修改?如何设置函数参数

10.SLTInsert(指定pos位置前插入)

思考点:10.1  空链表可以插入吗?和头插有区别吗?(头结点是否有改变?)

              10.2  如何找到pos的前一个结点?

11.SLTEraseAfter(删除pos位置后的结点)

思考点:11.1  删除的是最后一个元素,或者第一个元素后面没有元素的情况是否需要单独考虑?

              11.2  pos是否需要置空?

12.SLTErase(删除pos位置结点)

思考点:12.1  只有头结点的情况和头删有区别吗?(头结点是否有改变?)

              12.2  pos是否需要置空?

13.SLTDestroy(清空链表)

思考点:12.1  头结点是否有改变?

2.带头结点的单链表

由于带头结点的单链表操作上其实和不带头结点的类似,所以这里就不再展示所有功能的代码.

编写的代码风格主要和严蔚敏老师的《数据结构》一书一致.

链表声明:

1.CreateLinklist(创建单链表)

2.ShowLinklist(打印单链表)

3.SearchLinklist(给定pos下标,查找链表)

4.InsertLinklist(插入元素进入链表)

5.DeleteLinklist(删除下表为i的后一个结点)

四.带头双向循环链表实现

在实现单链表的时候,其实我们已经可以发现

单链表的优势不在尾插尾删,而在于头插和头删

那是否存在一个链表,无论是尾插尾删,或者是头插和头删,都能轻易实现呢?

答案就是我们接下来介绍的带头双向循环链表.

链表声明:

为了区分和单链表的数据类型,将数据域命名为LTDataType

和单链表不同,它有一个前驱指针prev,和一个后继指针next

 需要实现的函数功能大致有下面几个:

下面,我们将逐一介绍这些函数.

1.BuyListNode(创建新结点)

思考点: 1.1 申请结点是否一定可以成功?如果不成功,我们需要进行什么操作?

 

 2.LTInit(初始化链表)

思考点: 2.1 为什么我们要设计一个这样的函数?

               2.2  我们需要进行什么操作?头结点的prev和next指针初始化指向NULL可以吗?

 3.LTPrint(打印链表)

思考点: 3.1 打印链表的时候,可以直接移动头指针去打印吗?

               3.2  需要判断传入的链表指针是否为空吗?

               3.3 终止条件还是尾指针的next指向NULL吗?

4.LTPushBack(尾插)

思考点: 4.1  还需要逐一遍历去找尾部吗?

               4.2  需要判断传入的链表指针是否为空吗?

               4.3  初始化和它是否又有关系呢?

5.LTPopBack(尾删)

思考点: 5.1  还需要逐一遍历去找尾部吗?

               5.2  需要判断传入的链表指针是否为空吗?

               5.3  链表为空,是否可以进行尾删呢?如何判断链表为空?

6.LTPushFront(头插)

思考点: 6.1 先改前面,再改后面,这样可行吗?

PS:先改前,就找不到哨兵头结点后面的结点了.

                6.2 需要单独判断只有一个结点的情况吗?

 

7.LTPopFront(头删)

思考点: 7.1 链表为空,可以头删吗?如何判断链表为空?

PS:同上.

                7.2 需要单独判断只有一个结点的情况吗?

8.LTFind(找指定元素的指针)

思考点: 8.1 为什么要创建一个这样的函数?它的时间复杂度是多少?

9.LTInsert(在pos位置前插入新结点)

思考点: 9.1 联系函数8,如何找到pos位置呢?

               9.2 和头插一样,可以先改前面,再改后面吗?

有了函数 LTInsert后,我们其实就可以简化头插函数,使用时传phead->next进去即可.

10.LTErase(删除pos位置结点)

 有了函数 LTErase后,我们其实就可以简化头删函数,使用时传phead->next进去即可.

11.LTEmpty(判断链表是否为空)

思考点: 11.1 是判断尾指针的next为空吗?

12.LTSize(返回链表长度)

思考点: 12.1 可以指针相减吗?

PS:链表的存储结构决定不可能指针相减.

13.LTDestroy(摧毁链表)

思考点: 13.1 释放空间的同时,可以在函数内部将指针置为NULL吗?

PS:形参改变,实参不改变.

               13.2  可以直接释放掉phead吗?删除操作如何进行?

五.顺序表与链表的对比

两者所有的区别,其实就在于它们的存储结构是不一样的.

顺序表是一块连续空间进行存储,而链表则未必是连续的.

这就决定了顺序表可以随机访问,可以进行排序算法操作,缓存利用率高.但删除,插入等操作相应

时间复杂度也会更高.

而链表没有容量的概念,像我们实现的带头结点的双向循环链表,甚至可以做到头尾插,头尾删,

都是O(1)的复杂度.但随机访问,排序算法等等显然是不现实的.

两者各有优劣,如何使用,取决于我们当时所需.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值