单链表的实现

    在本次博客当中我们来实现单链表。首先来介绍以下单链表,我们的单链表和顺序表一样都属于线性表。在逻辑结构上我们的链表也是“连续”存储的。就像下图中所示的一样:

     为了使我们的每一个节点之间相互连接在一起所以在节点当中不能单纯的存储数据,我们还会空出一定的空间进行下一个节点的地址的存储。所以我们真实的链表的结构也就是物理结构如下:   通常情况下我们的节点类型分为存在哨兵节点的链表和不带哨兵节点的链表,不带哨兵节点的链表相对复杂,会了不带哨兵为节点的链表对于不带哨兵的链表也一定没什么问题,所以我们就来实现以下不带哨兵位节点的链表。

  我们通常情况下会创建一个phead指针用于找到我们第一个节点的位置,之后我们每一个节点的地址都会存储在节点当中的结构体指针当中,直到最后一个节点指向空指针为止。

  针对于我们单链表编写的顺序可以如下思维导图中体现:

  我们的链表是由一个有一个相同的结构组成的,所以我们需要定义一个结构体,结构体当中存放着数据值和我们的下一个结构体的指针,详细的代码如下:

  为了使我们之后的书写方便,所以我们将我们创建出来的结构体重命名为STLNode。和顺序表一样的道理为了使我们的存储的数据方便控制和改变,所以我们还可以使用宏定义将我们的int定义为DataType。在定义完上面的结构体之后我们就可以开始我们单链表代码的详细的书写了。

     🌵STLPrint函数

  首先我们通过代码进行了解链表的具体的运行操作:

  就像是我们上面的代码一样:我们向我们的函数传入我们头节点的地址,也就是我们第一个节点的地址。但是我们需要注意的是:最好不要对我们传入的指针进行直接的修改,否则会产生意想不到的隐患。所以我们创建了一个结构体指针cur接收我们的头节点的指针。对于结构体的使用比较熟悉的同学肯定会知道:我们可以通过 -> 准确的找到我们结构体当中所存储的数据并加以修改。并且根据我们链表的特点我们可以通过判断我们的节点的地址是否为NULL进而判断我们的节点是否遍历结束。所以我们可以将我们的循环条件设置为观察cur是否为空指针。并在循环体当中打印处相应节点当中存储的数据的值。至于循环条件的变化,因为我们循环的判断条件为cur所以就可以将cur每次修改为cur->next进行判断。(如果cur不为空就将cur更改为next,使得我们的节点向后走一位)总和上面的特点我们便有了以上的print代码。在完成了上面的print功能之后,我们再来完成向链表当中输入数据的函数STLPushBack。

     🌵STLPushBack函数

  单链表的主要目的就是存储数据,所以我们还要实现一个向单链表当中插入数据的函数,我们就先来实现一下尾插函数:

  对于这一部分我们就需要分开思考了,假如我们插入的链表为空链表呢?我们不能像我们正常的代码一样,我们只需要将我们的头指针指向我们新开辟的节点。如果不为空节点的话我们就需要找到链表的尾部,之后将我们新开辟的节点插入到我们的链表的末尾。开辟新的节点的函数如下:

   尾插函数功能的测试效果如下:

   在这一部分需要特别注意的是我们想要在我们的链表当中链接新的节点,就相当于想要改变一个指针,就需要将我们的结构体指针的指针作为参数传入函数当中,也就是向函数传入一个二级指针。尾插的代码就如上面所示。 

     🌵STLPushFront函数

  有了尾插的函数之后,肯定会有头插。对于头插函数我们少了找尾的步骤。

   逻辑也相对来说简单了很多,我们需要先创建一个新的节点,之后将我们的头节点的指针链接到我们新开的节点之后,最后将我们的头节点的值更改为我们的新节点即可。进行测试效果如下:

  对于头插函数来说我们不需要考虑是否链表为空指针的问题,因为当我们的链表为空指针,那么我们新节点的下一个结构体的目标地址还是指向空指针,并不会产生影响,所以我们无需修改我们的代码。 

     🌵STLPopBack函数

  有了向链表当中插入数据的函数,那么就一定会有删除的函数。具体的代码如下:

  需要我们注意的是当我们的链表为空链表的时候我们就不能在使用删除函数对链表当中的数据进行删除,所以我们使用断言使得我们向函数当中传入的指针不能为空。之后我们需要判断的是当我们的链表当中只存在一个节点的情况:如果链表当中只存在一个节点那么就将我们的头指针直接指向空即可。如果有多个地址的话就需要进行找尾操作,找到我们尾节点的前一个直接将该节点的next指针释放之后再置为空即可。上述代码的测试情况如下:

   就像是上面所示的那样,我们插入数据的顺序应该为 7->6->5->4->3->2->1 之后调四次尾删函数,删除了4->3->2->1 就得到了如上图所示的测试情况。

     🌵STLPopFront函数

  下一个我们再来实现以下头删函数:

  和我们的尾删一样,我们的链表为空的时候就不能继续删除元素了。我们只需要将我们的头指针指向next即可。但是需要注意这一步骤的代码书写的操作,假如我们先将我们的节点置空了那么我们结构体当中的内容就可能置为随机值(取决于随机值)就找不到next节点,所以我们不能直接这么进行操作。如果直接将我们的头节点指向next那么我们就找不到原本的头节点了,那么就会造成内存泄露的隐患,所以我们在这里采用一个结构体的变量,来保存我们原本头节点的地址,之后将我们的头节点指向next,最后释放我们的tmp指针即可。代码的测试情况如下:

     🌵STLFind函数

  在我们的链表当中我们还会有查找指定元素的函数,所以我们需要创建一个查找函数。我们将这个函数的返回值定义为返回该节点的指针,便于我们后面函数的复用:

  对于这个函数的实现思路其实是最简单的,我们只需要遍历整个链表一直查找看看我们节点存储的数据是否为我们指定的值即可。如果存在就需要返回我们该节点的指针,如果不存在就返回空指针。运行的结果如下: 

     🌵STLInsertAfter函数

  下一个功能是我们在某个特定的值后面插入指定的数据的函数。

  这一部分函数的实现同样很简单,我们需要先判断我们向函数当中查找的地址是否为空,如果不为空直接开辟一个空节点,再将我们空节点链接到我们的链表当中即可,就像上述代码所示。测试效果如下:

   我们在数据值为5的后面插入一个节点,节点当中存储的数据的值为11。 

     🌵STLErase函数

  之后就是我们删除某个特定值的函数:

  首先我们需要判断的是我们的是我们的单链表当中不为空还有pos节点的指针不为空。之后我们需要通过遍历链表找到我们pos位置的前一个节点的地址,之后将这一个节点的next直接连接到我们pos位置的next。之后释放掉我们pos位置的节点即可。代码的测试效果如下:

     🌵STLInsert函数

  我们一般在链表的插入操作分为在前面插入和在尾节点插入两种情况,所以由于在前面插入在单链表中效率很低,但是在我们的c++当中实现的插入的函数是在结点之前插入,(在系统库中实现的不是单链表)所以我们也将我们的在节点之前插入的函数插入链表当中:

  对于这个函数我们同样需要分情况进行判断:如果我们需要在第一个节点之前进行插入的话就相当于我们的头插函数,其中的逻辑也和我们的头插函数完全相同。如果我们不是在第一个位置插入元素那么我们就需要找我们pos节点的前一个位置,并将我们新开辟的节点插入到该节点之后,pos节点之前。代码的运行效果如下:

     🌵STLDestory函数

  我们单链表的最后一部分的函数也就是我们使用完单链表之后的销毁函数了:

  在这一部分我们需要注意的是:我们需要将我们的头节点的next先用一个结构体指针接收起来,之后再将我们的头节点释放,之后将我们的cur传给我们的头节点。

  以上就是我们单链表实现的全部内容了。感谢您的观看,再见。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿白逆袭记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值