C语言单链表的算法之插入节点

一:访问各个节点中的数据

        (1)访问链表中的各个节点的有效数据,这个访问必须注意不能使用p、p1、p2,而只能使用phead

        (2)只能用头指针不能用各个节点自己的指针。因为在实际当中我们保存链表的时候是不会保存各个节点的指针的,只能通过头指针来访问链表节点

        (3)前一个节点的pNEXT指针能帮助我们找到下一个节点

#include <stdio.h>        
#include <string.h>        
#include <stdlib.h>        
 
//构建一个链表的节点
struct node
{
 
    int datas;                      //有效数据
    struct node *pNEXT;             //指向下一个节点的指针
    
 
};
 
int main(void)
{
 
    //定义头指针
    struct node *phead = NULL;
 
    //创建一个链表节点
    struct node *p = (struct node *)malloc(sizeof(struct node));        
 
    if(NULL == p)                //检查申请结果是否正确
        {
            printf("malloc error!\n");        
 
            return -1;
 
        }
 
      //memset(p,0,sizeof(struct node));          
      bzero(p,sizeof(struct node));            //清理申请到的堆内存
        
      //填充节点
      p->datas = 1;                            //填充数据区
      p->pNEXT = NULL;                         //将来要执行下一个节点的首地址
        
      phead = p;                              //将本节点和前面的头指针关联起来
 
 
     //创建一个链表节点并和上一个节点关联起来
    struct node *p1 = (struct node *)malloc(sizeof(struct node));        
 
    if(NULL == p1)                //检查申请结果是否正确
        {
            printf("malloc error!\n");        
 
            return -1;
 
        }
 
      //memset(p,0,sizeof(struct node));          
      bzero(p1,sizeof(struct node));            //清理申请到的堆内存
        
      //填充节点
      p1->datas = 1;                            //填充数据区
      p1->pNEXT = NULL;                         //将来要执行下一个节点的首地址
        
      p-pNEXT>= p1;                            //将本节点和前面的节点关联起来
 
    
        //再创建一个链表节点并和上一个节点关联起来
    struct node *p2 = (struct node *)malloc(sizeof(struct node));        
 
    if(NULL == p2)                //检查申请结果是否正确
        {
            printf("malloc error!\n");        
 
            return -1;
 
        }
 
      //memset(p2,0,sizeof(struct node));          
      bzero(p2,sizeof(struct node));            //清理申请到的堆内存
        
      //填充节点
      p2->datas = 1;                            //填充数据区
      p2->pNEXT = NULL;                         //将来要执行下一个节点的首地址
            
      p1-pNEXT>= p2;                           //将本节点和前面的节点关联起来


      //访问链表节点的第一个节点的有效数据

        printf("phead->p->datas = %d\n",phead->datas);
        printf("p->datas =  %d\n",p->datas);
     
     //访问链表节点的第二个有效数据
        printf("phead->pNEXT->datas =%d\n",phead->pNEXT->datas);
        printf("p1->datas = %d\n",p1->datas);
   
     
     //访问链表节点的第三个有效数据
        printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->datas);
        printf("p2->datas = %d\n",p2->datas);


     



  
 
   
 
      return 0;
 
 
}

二:将创建的节点封装成一个函数

        (1)封装时的关键就是函数的接口(函数参数和返回值)的设计

           

#include <stdio.h>        
#include <string.h>        
#include <stdlib.h>        
 
//构建一个链表的节点
struct node
{
 
    int datas;                      //有效数据
    struct node *pNEXT;             //指向下一个节点的指针
    
 
};

/****************************************
函数名:create
作用:创建一个链表节点
返回值:p 指针,指针指向本函数创建的一个节点的首地址
参数:
****************************************/
struct node *create(int a)
{


    struct node *p = (struct node *)malloc(sizeof(struct node));

    if(NULL == p)
        {
            printf("malloc error!\n");

            return NULL;

        }

      memset(p,0,sizeof(struct node));  
      //bzero(p,sizeof(struct node));

      p->datas = a;
      p->pNEXT = NULL;


      return p;






 
int main(void)
{
 
    //定义头指针
    struct node *phead = NULL;
     
    
    //调用函数实现创建链表节点并将数据放到有效数据区                                
    phead = create(100);          //调用一次函数,创建一个链表节点返回节点首地址给头指针
    phead->pNEXT = create(200);   //再次创建一个链表节点返回节点首地址给上一个节点的指针 
    phead->pNEXT->pNEXT = create(300); //同上


    
    printf("phead->p->datas = %d\n",phead->datas);        
    //printf("p->datas =  %d\n",p->datas);        //使用功能函数创建链表节点就不能再使用
                                                  //节点指针访问有效数据区,只能通过头    
                                                  //指针                

    printf("phead->pNEXT->datas =%d\n",phead->pNEXT->datas);
    //printf("p1->datas = %d\n",p1->datas);

    printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->datas);
    //printf("p2->datas = %d\n",p2->datas);
      



  
 
   
 
      return 0;
 
 
}








}

 运行结果:

     

三:从链表头部插入新节点

  (1)思路:第一步:新节点的next指向原来的第一个节点

                      第二步:头指针的next指向新节点

                      第三步:头节点的计数要加一     

                     

  (2)代码示例:

        

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct node
{

    int datas;
    struct node *pNEXT;


};

struct node *create(int a)
{


    struct node *p = (struct node *)malloc(sizeof(struct node));

    if(NULL == p)
        {
            printf("malloc error!\n");

            return NULL;

        }

      memset(p,0,sizeof(struct node));  
      //bzero(p,sizeof(struct node));

      p->datas = a;
      p->pNEXT = NULL;


      return p;


}



void insert_tail(struct node *phead,struct node *new)
{

    struct node *p = phead;
    int cat = 0;
    while(NULL !=  p->pNEXT)
        {


            p = p->pNEXT;
            cat++;

        }

        p->pNEXT = new;
        phead->datas = cat +1;


}



void insert_head(struct node *head,struct node *new)
{

    new->pNEXT =  head->pNEXT;            //新节点的next指向之前的第一个节点

    head->pNEXT = new;                    //头节点的next指向新节点地址
    
    head->datas += 1;                    //头节点的计数加一

}



int main(void)
{
    struct node *phead = create(0);

    insert_head(phead,create(1));
    insert_head(phead,create(2));
    insert_head(phead,create(3));




    /*
    
            phead = create(100);
            phead->pNEXT = create(200);
            phead->pNEXT->pNEXT = create(300);

    */
    

        printf("phead->p->datas = %d\n",phead->datas);
        //printf("p->datas =  %d\n",p->datas);

        printf("phead->pNEXT->datas =%d\n",phead->pNEXT->datas);
        //printf("p1->datas = %d\n",p1->datas);

        printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->datas);
        //printf("p2->datas = %d\n",p2->datas);

        printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->pNEXT->datas);
        //printf("p2->datas = %d\n",p2->datas);

      return 0;


}

 运行结果:

        

四:从链表尾部插入新节点

        (1)思路:从头指针往后开始遍历指针指向的地址判断是不是NILL,直到最后一个节点,因为最后一个节点的是指向NULL的,所以遍历结束,然后将最后一个节点的指针指向新创建的节点

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


//构建一个链表的节点
struct node
{

    int datas;
    struct node *pNEXT;


};


//创建一个链表节点
struct node *create(int a)
{


    struct node *p = (struct node *)malloc(sizeof(struct node));

    if(NULL == p)
        {
            printf("malloc error!\n");

            return NULL;

        }

      memset(p,0,sizeof(struct node));  
      //bzero(p,sizeof(struct node));

      p->datas = a;
      p->pNEXT = NULL;


      return p;


}


//从尾部插入节点
void insert_tail(struct node *phead,struct node *new)
{

    struct node *p = phead;        //定义临时指针变量
    
    //循环遍历指针
    while(NULL !=  p->pNEXT)
        {


            p = p->pNEXT;

        }

        p->pNEXT = new;    //节点尾部指针指向新节点


}



int main(void)
{
    struct node *phead = create(100);        //由于实现尾部插入判断是否是最后一个节点
                                             //所以如果将头指针指向NULL那么程序就出错了
                                             //这里就先创建一个节点让头指针指向它

    insert_tail(phead,create(200));          //再从单链接尾部插入节点
    insert_tail(phead,create(300));          //同上


    /*
    
            phead = create(100);
            phead->pNEXT = create(200);
            phead->pNEXT->pNEXT = create(300);

    */
    

        printf("phead->p->datas = %d\n",phead->datas);
        //printf("p->datas =  %d\n",p->datas);

        printf("phead->pNEXT->datas =%d\n",phead->pNEXT->datas);
        //printf("p1->datas = %d\n",p1->datas);

        printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->datas);
        //printf("p2->datas = %d\n",p2->datas);

      return 0;


}

(2)问题:因为我们在inserttail中直接默认了头指针指向的有一个节点,因此如果程序中直接
定义了头指针后就直接insert tail就会报段错误。我们不得不在定义头指针之后先create_node
创建一个新节点给头指针初始化,否则不能避免这个错误;但是这样解决让程序看起来逻辑有点
不太顺,因为看起来第一个节点和后面的节点的创建、添加方式有点不同。 

(3)链表还有另一种用法,就是把头指针指向的第一个节点作为头节点使用。头节点的特点是它紧跟在头指针后面;它的数据区是空的(有时候不是空的,用来存储链表节点个数);指针部分指向下一个节点(第一个链表节点)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


//构建一个链表的节点
struct node
{

    int datas;
    struct node *pNEXT;


};


//创建一个链表节点
struct node *create(int a)
{


    struct node *p = (struct node *)malloc(sizeof(struct node));

    if(NULL == p)
        {
            printf("malloc error!\n");

            return NULL;

        }

      memset(p,0,sizeof(struct node));  
      //bzero(p,sizeof(struct node));

      p->datas = a;
      p->pNEXT = NULL;


      return p;


}


//从尾部插入节点
void insert_tail(struct node *phead,struct node *new)
{

    struct node *p = phead;        //定义临时指针变量
    int cat = 0;                   //创建一个计数器变量 
    
    //循环遍历指针
    while(NULL !=  p->pNEXT)
        {


            p = p->pNEXT;
            cat++;                 //每循环一次加一

        }

        p->pNEXT = new;    //节点尾部指针指向新节点
        phead->datas = cat +1;


}



int main(void)
{
    struct node *phead = create(0);          //由于实现尾部插入判断是否是最后一个节点
                                             //所以如果将头指针指向NULL那么程序就出错了
                                             //这里就先创建一个头节点让头指针指向它

    insert_tail(phead,create(1));          
    insert_tail(phead,create(2));
    insert_tail(phead,create(3));
             


    /*
    
            phead = create(100);
            phead->pNEXT = create(200);
            phead->pNEXT->pNEXT = create(300);

    */
    

        printf("phead->p->datas = %d\n",phead->datas);            //节点数
      

        printf("phead->pNEXT->datas =%d\n",phead->pNEXT->datas);  //第一个节点数据区数据
   

        printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->datas);   //第二个
   
        
         printf("phead->pNEXT->datas = %d\n",phead->pNEXT->pNEXT->pNEXT->datas);  //三
        

      return 0;


}

运行 结果:链表节点数 3   第一个节点数据区为1   第二个为2 第三个为3

(4)这样看来,头节点确实和其他节点不同。我们在创建一个链表时添加节点的方法也不同。头
节点在创建头指针时一并创建并且和头指针关联起来;后面的真正的存储数据的节点用节点添加
的函数来完成,譬如insert tail。

(5)链表有没有头节点是不同的。体现在链表的插入节点、删除节点、遍历节点、解析链表的各
个算法函数都不同。所以如果一个链表设计的时候就有头节点那么后面的所有算法都应该这样来
处理:如果设计时就没有头节点,那么后面的所有算法都应该按照没有头节点来做。实际编程中
两种链表都有人用,所以程序员在看别人写的代码时一定要注意看它有没有头节点

注意:注意在写代码过程中的箭头符号,和说话过程中的指针指向。这是两码事,容易搞混箭头符号实质是用指针来访问结构体,所以箭头的实质是访问结构体中的成员。清楚的来说程序中的箭头和链表的连接没有任何的关系;链表中的节点通过指针指向来连接,编程中表现为赋值语句(用=来连接),实质是把最后一个节点的首地址,赋值给前一个节点中的pnext元素作为值

链表可以从头部插入,也可以尾部插入,也可以两头插入。对链表来说几乎没有差别,但是有时候对业务逻辑有差别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/758851.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

后端之路第三站(Mybatis)——XML文件操作sql

一、XML映射文件是啥 前面我们学过了在Mapper接口用注解的方式来操作sql语句 那么XML映射文件就另一种操作sql语句的方法 为什么还要有这么个玩意&#xff1f; 我简单说就是&#xff1a;如果有的sql特别复杂的话&#xff0c;比如需要【动态sql】的话&#xff0c;就得用到XM…

数据可视化期末总结

期末考试重点&#xff08;世界上最没意义的事情&#xff09; 选择 p8 数据可视化的标准&#xff1a; 实用、完整、真实、艺术、交互&#xff08;性&#xff09; p21 色彩三属性 色相、饱和度、亮度 p23 视觉通道的类型&#xff1a; 记得色调是定性 p39 散点图&#xff08;二维…

GIT-LFS使用

0.前言 目前git仓库有很多很大的文件需要管理&#xff0c;但是直接上传&#xff0c;每次clone的文件太大&#xff0c;所有准备使用git-lfs解决。 1、下载和安装 Git LFS 1.1、直接下载二进制包&#xff1a; Releases git-lfs/git-lfs GitHub 安装 Git LFS sudo rpm -ivh…

Leica Cyclone 3DR2024 一款功能强大的点云建模软件下载License获取

Leica Cyclone 3DR 2024 是一款功能强大的点云建模软件&#xff0c;使用旨在为用户提供全面的点云管理、自动化的点云分析&#xff0c;结合强大的建模&#xff0c;在一个直观友好的环境中&#xff0c;专注的完成挑战&#xff0c;提高生产力&#xff0c;轻松创建并交付专业的成果…

杨幂跨界学术圈:内容营销专家刘鑫炜带你了解核心期刊的学术奥秘

近日&#xff0c;知名艺人杨幂在权威期刊《中国广播电视学刊》上发表了一篇名为《浅谈影视剧中演员创作习惯——以电视剧<哈尔滨一九四四>为例》的学术论文&#xff0c;此举在学术界和娱乐圈均引起了广泛关注。该期刊不仅享有极高的声誉&#xff0c;还同时被北大中文核心…

Data-Driven Reinforcement Learning for Robotic Manipulation

意思是 不同的任务以及机器人都有单独的数据和模型 未来需要整合 一个大的数据集包含所有的 然后训练一个大模型 以后具体的任务只需要针对这个模型进行微调 challenge bootstrapping with large data 2 3 4 高清图补充

【C++】using namespace std 到底什么意思

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文作为 JohnKi 的学习笔记&#xff0c;引用了部分大佬的案例 &#x1f4e2;未来很长&a…

【SGX系列教程】(二)第一个 SGX 程序: HelloWorld,linux下运行

文章目录 0. SGX基础原理分析一.准备工作1.1 前提条件1.2 SGX IDE1.3 基本原理 二.程序设计2.1 目录结构2.2 源码设计2.2.1 Encalve/Enclave.edl:Enclave Description Language2.2.2 Enclave/Enclave.lds: Enclave linker script2.2.3 Enclave/Enclave.config.xml: Enclave 配置…

ctfshow-web入门-命令执行(web59-web65)

目录 1、web59 2、web60 3、web61 4、web62 5、web63 6、web64 7、web65 都是使用 highlight_file 或者 show_source 1、web59 直接用上一题的 payload&#xff1a; cshow_source(flag.php); 拿到 flag&#xff1a;ctfshow{9e058a62-f37d-425e-9696-43387b0b3629} 2、w…

MathType7.6专业数学公式编辑器!与Word、PPT等常用软件无缝对接。

MathType&#xff0c;一款专业的数学公式编辑器&#xff0c;以其强大的功能和友好的用户界面&#xff0c;在科研、教学等领域广受欢迎。它支持丰富的数学符号和公式模板&#xff0c;满足不同用户的需求。同时&#xff0c;MathType还提供了多种输出格式&#xff0c;方便与其他文…

3ds Max导出fbx贴图问题简单记录

1.前言 工作中发现3ds Max导出的fbx在其它软件&#xff08;Autodesk viewer&#xff0c;blender&#xff0c;navisworks&#xff0c;FBXReview等&#xff09;中丢失了部分贴图&#xff0c;但导出的fbx用3ds Max打开却正常显示。 fbx格式使用范围较广&#xff0c;很多常见的三…

如何用Go语言,实现基于宏系统的解释器?

目录 一、Go语言介绍二、什么是宏系统三、什么是解释器四、如何用Go语言实现一个基于宏系统的解释器&#xff1f; 一、Go语言介绍 Go语言&#xff0c;又称为Golang&#xff0c;是一种由谷歌公司开发并开源的编程语言。Go语言的设计目标是提高程序员的生产力&#xff0c;同时具…

树莓派开发之文件传输

文章目录 一、简介使用U盘传输文件使用SD卡传输文件使用Xftp 7传输文件 二、 总结 一、简介 在树莓派开发中经常会用到文件传输&#xff0c;下面介绍几种树莓派文件传输的几种方法。 使用U盘传输文件 &#xff08;1&#xff09;复制所需传输文件到U盘 &#xff08;2&#…

详细介绍MySQL的索引(上)

索引 索引概述 索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用(指向数据&#xff0c;这样就可以在这些数据结构上实现高级查找算法&#xff0c;这种数据结…

【计算机图形学】期末考试知识点汇总(上)

文章目录 视频教程第一章 计算机图形学概述计算机图形学的定义计算机图形学的应用计算机图形学 vs 图像处理 vs模式识别图形显示器的发展及工作原理理解三维渲染管线 第二章 基本图元的扫描转换扫描转换直线的扫描转换DDA算法Bresenham算法中点画线算法圆的扫描转换中点画圆算法…

json文件 增删查改

默认收藏夹 qt操作json格式文件... 这个人的 写的很好 我的demo全是抄他的 抄了就能用 —————————— 下次有空把我的demo 传上来 在E盘的demo文件夹 json什么名字

小迪安全v2023笔记 1-18

小迪安全v2023笔记 1-18 棱角社区 文章目录 1. 基础入门1. 正向shell与反向shell2. web应用3. 抓包&#xff0c;封包&#xff0c;协议&#xff0c;app&#xff0c;小程序&#xff0c;pc应用&#xff0c;web应用 2. 信息打点1. 常见信息获取2. 文件泄露3. 常见阻碍4. CDN绕过&a…

二叉树第二期:堆的实现与应用

若对树与二叉树的相关概念&#xff0c;不太熟悉的同学&#xff0c;可移置上一期博客 链接&#xff1a;二叉树第一期&#xff1a;树与二叉树的概念-CSDN博客 本博客目标&#xff1a;对二叉树的顺序结构&#xff0c;进行深入且具体的讲解&#xff0c;同时学习二叉树顺序结构的应用…

电子电路学习笔记(3)三极管

部分内容参考链接&#xff1a; 电子电路学习笔记&#xff08;5&#xff09;——三极管_三极管 箭头-CSDN博客 模拟电子技术基础笔记&#xff08;4&#xff09;——晶体三极管_集电结的单向导电性-CSDN博客 硬件基本功-36-三极管Ib电流如何控制Ic电流_哔哩哔哩_bilibili 部分…

栈的实现

栈 1.栈的概念及结构 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端插入和删除元素。进行插入和删除的一端称为栈顶&#xff0c;另一端称为栈底。栈中的元素支持先进后出的原则。 2.栈的实现 栈的实现一般使用数组和链表&#xff0c;相对而言使用数组更优一些&…