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

news2024/11/19 19:32:57

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

        (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.coloradmin.cn/o/1867160.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么使用python进行整除取余求幂

怎么使用python进行整除取余求幂&#xff1f; 整除法是//&#xff0c;称为地板除&#xff0c;两个整数的除法仍然是整数。 10//33 3 求模运算是%&#xff0c;相当于mod&#xff0c;也就是计算除法的余数。 5%2 1 求幂运算使用两个连续的*&#xff0c;幂运算符比取反的优先级高…

操作系统面试篇一

很多读者抱怨计算操作系统的知识点比较繁杂&#xff0c;自己也没有多少耐心去看&#xff0c;但是面试的时候又经常会遇到。所以&#xff0c;我带着我整理好的操作系统的常见问题来啦&#xff01;这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如 用户态和内核态、系统…

css3实现水纹进度条

其实有一个mask-image属性 挺有意思&#xff0c;在元素上面实现遮罩层的效果&#xff0c;不过这玩意有些兼容性问题 需要处理&#xff0c;所以单纯可以通过渐变色的方式来实现 同时加上动画效果 .jianbian {width: 100%;height: 16px;background-color: #eee;display: flex;bor…

【shell 学习一】shell执行方式以及变量(自定义变量、整数运算)定义

1.shell执行方式 测试脚本 vim file1 echo hello 2024 read -p 请输入 name echao hh,$name执行1 bash file1执行2 sh file1执行3 . file1执行4 source file11和2的方式&#xff0c;是子shell 3和4的方式&#xff0c;是本shell bash是进入新的命令 这时候退出edit是退出这个新…

docker部署FastDFS整合Springboot

文章目录 1、FastDFS是什么&#xff1f;2、搭建docker环境3、部署fastdfs4、整合springboot5、接口测试参考文章 1、FastDFS是什么&#xff1f; FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文…

树莓派4B_OpenCv学习笔记13:OpenCv颜色追踪_程序手动调试HSV色彩空间_检测圆

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; OpenCv颜色追踪_程序手动调试HSV色彩空间_检测灰度图中的…

Windows USB设备驱动开发 - 常见概念的解释

我们听到许多 USB 术语几乎交替抛出。 它们都是什么意思&#xff1f;假设我们看到类似 “多亏了 USB 3.0&#xff0c;我可以将 SuperSpeed U 盘连接到电脑的 xHCI 主机控制器&#xff0c;并更快地复制文件。” 让我们了解该句子中的 USB 术语。 USB 3.0、USB 2.0 和 USB 1.0 请…

C语言 | Leetcode C语言题解之第200题岛屿数量

题目&#xff1a; 题解&#xff1a; void cleanLand(char** grid, int gridSize, int ColSize,int row,int column) {if(grid[row][column] 1){//不等于1则清零grid[row][column] 0;}else{//不等于1则返回return ;}int newRow;int newColumn;//上if(row ! 0) //还能上{ne…

关于 AD21导入电子元器件放置“3D体”STEP模型失去3D纹理贴图 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139969415 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

java Logback 日志格式参数详细说明

logback 打印日志格式介绍官网 Springboot [日志管理LogBack] java Logback 日志格式参数详细说明 打印日志格式&#xff1a; <property name"LOG_PATTERN" value"[${APP_NAME} ${SERVER_IP}:${SERVER_PORT}] %d{yyyy-MM-dd HH:mm:ss.SSS} %level ${PID} …

【项目实训】面试经验总结页面编写(前期探索)

首先&#xff0c;我们想尝试编写和chatchat项目兼容的页面展示部分&#xff0c;在编写成功之后&#xff0c;由于streamlit库是一个很强大的python库&#xff0c;比较死板&#xff0c;做出的页面并不尽如人意&#xff0c;于是我们放弃了这种方法。但这也是探索前端页面展示形式的…

【Android面试八股文】Framework面试:ThreadLocal的原理,以及在Looper是如何应用的?

文章目录 ThreadLocal的原理,以及在Looper是如何应用的?ThreadLocal 是什么ThreadLocalMap是什么?ThreadLocal在Looper中的应用**总结**扩展阅读ThreadLocal的原理,以及在Looper是如何应用的? ThreadLocal 可以把一个对象保存在指定的线程中,对象保存后,只能在指定线程…

找不到模块“@/stores/xxx”或其相应的类型声明。

找不到模块“/stores/xxx”或其相应的类型声明。 问题图示 在 Vue3 项目中&#xff0c;特别是在使用 TypeScript 时&#xff0c;/ 符号通常是用来代表项目根目录的别名。例如&#xff0c;/stores/xxx 可能是指向你项目中的某个 store 文件或目录。出现找不到模块的错误通常是由…

FastGPT 调用Qwen 测试Hello world

Ubuntu 安装Qwen/FastGPT_fastgpt message: core.chat.chat api is error or u-CSDN博客 参考上面文档 安装FastGPT后 登录&#xff0c; 点击右上角的 新建 点击 这里&#xff0c;配置AI使用本地 ollama跑的qwen模型 问题&#xff1a;树上有3只鸟&#xff0c;开了一枪&#…

【C++】内存分区

目录 内存分区代码运行前后区别各分区详细解释C内存申请和释放 内存分区 不同的操作系统对程序内存的管理和划分会有所不同。 此处是C内存区域划分主要是针对通用的情况&#xff0c;并不限定在某个特定操作系统上 一般分为4个区&#xff08;有时把全局区拆分成数据区未初始化…

Git企业开发---初识Git

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 引言 不知道大家有没有经历这种困惑&#xff0c;当我们要去交某文档时&#xff0c;总是要进行修改&#xff0c;修改后再交…

python3用两个栈实现一个队列

栈与队列 栈&#xff1a;先入后出&#xff0c;First In First Out (FIFO) &#xff0c;类似桶&#xff08;入到桶底、取从桶顶&#xff09; 队列&#xff1a;先入先出&#xff0c;First In Last Out (FILO) 用两个栈实现一个队列 两个桶&#xff08;栈&#xff09;&#x…

linux centos rabbitmq3.7.5 一键安装部署

linux centos rabbitmq3.7.5 一键安装部署 一、基础理论二、kafka和rocketmq、rabbitmq的区别三、下载所需安装包四、一键安装 一、基础理论 RabbitMQ是一个实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff0c;其作用主要体现在以下几个方面&a…

K8S -理解StatefulSet - 部署有状态应用

什么是 有状态服务和 无状态服务 有状态服务&#xff08;Stateful Service&#xff09;&#xff1a; 有状态服务是指在处理请求期间维护和跟踪用户状态或会话信息的服务。这意味着服务在多个请求之间保持状态&#xff0c;并且需要在请求之间共享和使用这些状态信息。通常&…

常微分方程算法之编程示例五(阿当姆斯法)

目录 一、研究问题 二、C代码 三、计算结果 一、研究问题 本节我们采用阿当姆斯法&#xff08;Adams法&#xff09;求解算例。 阿当姆斯法的原理及推导请参考&#xff1a; 常微分方程算法之阿当姆斯法&#xff08;Adams法&#xff09;_四步四阶adams显格式;三步四阶adams隐…