根据《C和指针》中讲解链表的知识,记录最终写一个在单链表中插入一个新节点的函数的过程,这个分析过程十分的有趣,准备了两篇博文,用于记录这个过程。


链表是以结构体和指针为基础的,所以结构体和指针是需要首先掌握的知识,掌握之后,最后要明白这个问题:结构体的自引用

这时候就可以尝试链表的学习了。记得去年学习链表的时候觉得特别新奇,并使用之写了一个蹩脚的学生信息管理系统,当然不值一提,可惜没有趁热打铁继续下去,也许不是坏事,醒悟的时候还不算晚!


先介绍下链表吧:

链表(linked list)就是一些包含数据的节点的集合。这些节点是用结构体定义的,如下:

typedef struct NODE{struct NODE *link;int value;} Node;

使用typedef定义了一个结构类型的别称Node,称为节点。

链表中的每个节点通过指针(也叫链)链接一起。程序通过指针访问链表中的节点。通常节点是动态分配的,但是有时你也能看到由节点数组创建的链表。即使在这种情况下,程序也是通过指针来遍历链表的。我们关注的是动态内存分配来创建节点。

在单链表中,每个节点包含一个指向链表下一个节点的指针。链表最后一个节点的指针字段的值为NULL,提示链表后面不再有其他节点。在你找到链表的第一个节点后,指针就可以带你访问剩余的所有节点。为了记住链表的起始位置,可以使用一个根指针(root pointer)。根指针指向链表的第一个节点。注意根指针只是一个指针,它不包含任何数据。

下面是一个单链表的图:

从上面的图中可以看出,这些节点相邻在一起,这是为了显示链表所提供的逻辑顺序。事实上,链表中的节点可能分布于内存中的各个地方。对于一个处理链表的程序而言,各节点在物理上是否相邻并没有什么区别,因为程序始终用指针(链)从一个节点移动到另一个节点。

但链表可以通过链从开始位置遍历链表直到结束位置,但链表无法从相反的方向进行遍历。

上面显示的链表中,节点根据数据的值按升序链接在一起。对于有些应用程序而言,这种顺序非常重要,比如根据一天的时间安排约会。对于那些不要求排序的应用程序,当然也可以创建无序的单链表。


下面正式进入正题:在单链表中插入一个新节点的第一次尝试:

如何才能把一个新节点插入到一个有序的单链表中呢?

假定我们有一个新值,比如12,想把它插入到上面提到的那个链表中。从概念上讲,是很容易做到的,从链表的起始位置开始,跟随指针直到找到第1个值大于12的节点,然后把这个新值插入到那个节点之前的位置。

实际的算法比较有趣:我们按顺序访问链表,当到达内容为15的节点(第一个值大于12的节点)时就停下来。我们知道这个新值应该添加到这个节点之前,但前一个节点的指针必须进行修改以实现这个插入。但是我们已经越过了这个节点,无法返回。解决这个问题的办法是始终保存一个指向链表当前节点之前的那个节点的指针。

了解这么多,我们就可以开始实践了:

首先定义一个头文件:

typedef struct NODE{struct NODE *link;int value;} Node;

存放于sll_node.h的头文件中。

下面开发一个函数,把一个节点插入到一个有序的单链表中,后面并做出详细分析:

//插入到一个有序的单链表。函数的参数是一个指向链表第一个节点的指针以及需要插入的值
#include <stdlib.h>
#include <stdio.h>
#include "sll_node.h"   //这个头文件是前面自己创建的#define FALSE 0
#define TRUE 1int sll_insert( Node *current, int new_value )
{Node *previous;Node *new;                //需要插入的新节点//寻找正确的插入位置,方法是顺序访问链表,直到到达其值大于或等于新插入的节点的值while( current->value < new_value ){previous = current; //始终保存当前节点之前的那个节点current = current->link; //当前节点移动到下一个节点}//为新节点分配内存,并把新值存储到新节点中,如果内存分配失败,函数返回FALSEnew = ( Node *)malloc( sizeof( Node ) );if( new == NULL ){return FALSE;}new->value = new_value;//把新节点插入到链表中,并返回TRUEnew->link = current; //新节点的指针指向当前节点previous->link = new; //前一个节点的指针指向新节点return TRUE;}

我们用下面的方法调用这个函数:

result = sll_insert( root, 12 );

下面跟踪代码的执行过程:

首先传递给函数的参数是root变量的值,它是指向链表的第一个节点的指针。当函数刚刚执行时,链表的状态如下:

这张图没有显示root变量,因为函数不能访问它。它的值的一份拷贝作为形参current传递给函数,但函数不能访问root。现在current->value是5,小于12,所以循环再次执行。

当我们回到循环的顶部时,current和previous指针都向前移动了一个节点。

现在current->value的值是10,小于12,因此循环体继续执行,结果如下:

现在current->value的值是15大于12,所以退出循环。

此时,重要的是previous指针,因为它指向我们必须加以修改以插入新值的那个节点。但首先,我们必须得到一个新节点,用于容纳新值。下面这张图显示了新值被赋值到新节点之后链表的状态。

把这个新节点链接到链表中需要两个步骤。

首先,new->link = current;

也就是使新节点指向将称为链表下一个节点的节点,也就是我们找到的第一个值大于12的那个节点。在这个步骤之后,链表的内容为:

第二个步骤是让previous指针所指向的节点修改为指向这个新节点。下面这个语句执行这个任务:

previous->link = new;

这个步骤之后,链表的状态如下:

然后函数返回,链表最终的样子如下:

从根节点开始,随各个节点的link字段逐个访问链表,我们可以发现这个新节点已被正确地插入链表中。


最后,不得不提的是现实是否就是这么如意?

可以思考一下,如果试图把20插入链表,也就是new_value = 20,这个程序还能正常工作吗?

这里把循环提出来:

while( current->value < new_value )
    {
        previous = current; //始终保存当前节点之前的那个节点
        current = current->link; //当前节点移动到下一个节点
    }

你会发现,while循环会越过链表的尾部,并对一个NULL指针执行间接访问操作。这是不合法的。

为了解决这个问题, 我们必须对current的值进行测试,在执行current->value之前确保它不是一个NULL指针:

将while语句的中条件换成如下:

while( current != NULL & current->value < new_value )

{

...

}

就解决了上述问题。

最后呢?提出下一篇博文要讲的内容,就是试试把3这个值插入链表,看看会发生什么?下篇博文见!

下篇博文地址: 【 C 】在单链表中插入一个新节点的尝试(二)

【 C 】在单链表中插入一个新节点的尝试(一)相关推荐

  1. 【 C 】在单链表中插入一个新节点的尝试(二)

    在上篇博文中:[ C ]在单链表中插入一个新节点的尝试(一),我们最后提到了如果向单链表的开头(起始位置)插入一个节点,上篇博文中给出的程序显然完成不了这任务. 这篇博文中,我们将解决这个问题,给出一 ...

  2. 【 C 】在双链表中插入一个新值的简明程序

    上两篇博文讲了如何在单链表中插入一个值: [ C ]在单链表中插入一个新节点的尝试(一) [ C ]在单链表中插入一个新节点的尝试(二) 这篇博文讲解如何在双链表中插入一个值. 单链表的替代方案就是双 ...

  3. 【 C 】简化双链表插入函数(对在双链表中插入一个新值的简明程序的简化)

    目录 背景 第一个技巧是语句提炼(statement factoring) 第二个简化技巧 最终简化版本 背景 上篇博文:[ C ]在双链表中插入一个新值的简明程序,讲了一个简明的双链表插入函数,那个 ...

  4. 数据结构例16.试设计一个算法, 使得在一个有序的单链表中插入一个元素后仍然有序。

    /* 16.试设计一个算法, 使得在一个有序的单链表中插入一个元素后仍然有序. */ # include <iostream> # include <stdlib.h> # i ...

  5. 通过一趟遍历找出长度为n的单链表中值最大的节点.【数据结构】【单链表】

    编写一个函数完成如下功能:通过一趟遍历找出长度为n的单链表中值最大的节点. 要求,在主函数中调用上面的函数测试. 提示:还需要定义其他函数,比如初始化链表,构造单链表,输出单链表. 输出结果: 代码展 ...

  6. 链表相关操作:创建链表、遍历链表、求链表长度、链表中删除一个节点、链表中插入一个节点、反转单链表...

    1 #include<iostream> 2 #include<stdlib.h> 3 4 typedef struct node 5 { 6 int data; 7 stru ...

  7. java数据结构 -链表 -获取有效节点个数,单链表中倒数k个节点

    // 1.获取到单链表的节点的个数(如果有头结点,不统计头结点)public static int getLength(HeroNode head){if (head.next == null){re ...

  8. 链表问题18——向有序的环形单链表中插入新节点

    题目 一个环形链表从头节点开始的顺序为不降序的顺序,也就是如1->2->2->2->3->4这样的链表叫做不降序,同时由最后的节点指回头节点.给定这个环形单链表的头节点h ...

  9. Coding Interview Guide -- 向有序的环形单链表中插入新节点

    [题目] 一个环形单链表从头节点head开始不降序,同时由最后的节点指回头节点.给定这样一个环形单链表的头节点head和一个整数num,请生成节点值为num的新节点,并插入到这个环形链表中,保证调整后 ...

最新文章

  1. 企业架构的过去、现在与未来
  2. 电商618背后的那些技术事儿
  3. 浅谈Java throw, throws, try catch异常处理
  4. java基础Java主类结构 2
  5. Entity Framework异步查询和保存
  6. boost::mp11::mp_set_intersection相关用法的测试程序
  7. 五轴加工的RTCP技术
  8. python方法定义..._解析Python类中的方法定义
  9. linux getline函数用法,Linux文本处理三剑客之awk学习笔记05:getline用法详解
  10. 如何写出整洁规范的R代码?是时候讨论一下代码规范性了
  11. 非华为电脑安装华为电脑管家
  12. 使用示波器测量运放带宽和压摆率
  13. Android存储管理
  14. html字体外围有黄色边框,input或textarea在chrome、safari浏览器点击获得焦点时出现黄色边框去掉的方法...
  15. Axure快速入门教程
  16. PDF Redactor - 涂黑屏蔽PDF文字让敏感内容不可读的软件工具
  17. ati jti jwt 和_JWT jti和kid属性的说明
  18. 珍藏多年的神网站都捐了,你需要的我都有,有了这些网站工作生活不用愁(二)
  19. java多模块项目脚手架:Spring Boot + MyBatis 搭建教程
  20. 资讯_邮件基本常识普及(to/cc/bcc) ;

热门文章

  1. 转:Swing中的线程探究
  2. java 图片传输方式_Java图像传输方法
  3. 分析手机网站的优势思维结构图_写了100多篇原创文章,我常用的在线工具网站推荐给大家...
  4. ue4 导出模型_UE4构建光照后模型变黑,二套UV解决办法
  5. pythonfor循环嵌套_python-嵌套和for循环中的字典和列表中的项...
  6. Android10剪贴板,剪纸堆 Clip Stack - 轻量级剪贴板管理程序(支持 Android 10)
  7. 直线宽度2 points wide_OpenGL 绘图实例二之直线和圆弧的绘制
  8. 关于Nginx的使用
  9. 如何在TEASOFT中加入直线与曲线?
  10. 2021年春季学期-信号与系统-第八次作业参考答案-第五小题