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.rhkb.cn/news/367822.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

偏微分方程笔记

极小位能原理&#xff1a; C 2 C^2 C2 是一个集合符号&#xff0c;表示所有二阶连续可微函数的集合 弱导数 C 2 C^2 C2 是一个集合符号&#xff0c;表示所有二阶连续可微函数的集合。 C 0 ∞ ( I ) C^{\infty}_0(I) C0∞​(I)表示于 I I I上无穷可微&#xff0c;且在端点a&…

使用pyinstaller 如何打包python项目

参考&#xff1a;【python项目正确打包方法-哔哩哔哩】 https://b23.tv/EDB6zbG Pyinstaller 详解多种打包过程(去坑,填坑)。_pyinstaller -f -w-CSDN博客 1.打开命令提示符&#xff1a; 找到python项目所在位置&#xff0c;输入cmd即可 2. 安装pipenv: 在命令提示符&#…

【强化学习的数学原理】课程笔记--2(贝尔曼最优公式,值迭代与策略迭代)

目录 贝尔曼最优公式最优 Policy求解贝尔曼最优公式求解最大 State Value v ∗ v^* v∗根据 v ∗ v^* v∗ 求解最佳 Policy π ∗ \pi^* π∗一些证明过程 一些影响 π ∗ \pi^* π∗ 的因素如何让 π ∗ \pi^* π∗ 不 “绕弯路” γ \gamma γ 的影响reward 的影响 值迭…

UiPath+Appium实现app自动化测试

一、环境准备工作 1.1 完成appium环境的搭建 参考&#xff1a;pythonappiumpytestallure模拟器(MuMu)自动化测试环境搭建_appium mumu模拟器-CSDN博客 1.2 完成uipath的安装 登录官网&#xff0c;完成注册与软件下载安装。 UiPath业务自动化平台&#xff1a;先进的RPA及自动…

Visual Studio 中的键盘快捷方式

1. Visual Studio 中的键盘快捷方式 1.1. 可打印快捷方式备忘单 1.2. Visual Studio 的常用键盘快捷方式 本部分中的所有快捷方式都将全局应用&#xff08;除非另有指定&#xff09;。 “全局”上下文表示该快捷方式适用于 Visual Studio 中的任何工具窗口。 生成&#xff1…

第十四章 集合(List)

一、集合框架体系 集合&#xff1a; &#xff08;1&#xff09;可以动态保存任意多个对象 &#xff08;2&#xff09;提供了一系列方便的操作对象的方法 二、Collection 1. Collection 接口常用方法 &#xff08;1&#xff09;add&#xff1a;添加单个元素 &#xff08;2…

Cypress测试:7个快速解决问题的调试技巧!

以下为作者观点&#xff1a; 快速编写代码是一项宝贵的技能&#xff0c;但能够有效调试和解决错误和bug&#xff0c;更是一个软件开发人员具有熟练技能的标志。调试是开发过程中的一个关键环节&#xff0c;可以确保软件按预期运行并满足用户需求。 Cypress 调试简介 Cypress …

【Linux】—Xshell、Xftp安装

文章目录 前言一、下载Xshell、Xftp二、安装Xshell三、使用XShell连接Linux服务器四、修改windows的主机映射文件&#xff08;hosts文件&#xff09;五、远程连接hadoop102/hadoop103/hadoop104服务器六、安装Xftp 前言 XShell远程管理工具&#xff0c;可以在Windows界面下来访…

uniapp + vue3 + Script Setup 写法变动 (持续更新)

一、uniapp 应用生命周期&#xff1a; https://uniapp.dcloud.net.cn/tutorial/vue3-composition-api.html 注意&#xff1a; 应用生命周期仅可在App.vue中监听&#xff0c;在其它页面监听无效。 二 、uniapp页面生命周期&#xff1a; https://uniapp.dcloud.net.cn/tutori…

3.js - 色调映射(renderer.toneMapping)

// ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } from three/examples/jsm/libs/lil-gui.module.min.js// 导入tween import * as TWEEN…

昇思25天学习打卡营第11天|基于MindSpore通过GPT实现情感分类

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) 基于MindSpore通过GPT实现情感分类 %%capture captured_output # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mindspore的版本号 !pip uninsta…

【目标检测】DINO

一、引言 论文&#xff1a; DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection 作者&#xff1a; IDEA 代码&#xff1a; DINO 注意&#xff1a; 该算法是在Deformable DETR、DAB-DETR、DN-DETR基础上的改进&#xff0c;在学习该算法前&#…

决策树算法的原理与案例实现

一、绪论 1.1 决策树算法的背景介绍 1.2 研究决策树算法的意义 二、决策树算法原理 2.1 决策树的基本概念 2.2 决策树构建的基本思路 2.2 决策树的构建过程 2.3 决策树的剪枝策略 三、决策树算法的优缺点 3.1 决策树算法的优势 3.2 决策树算法的局限性 3.3 决策树算…

Vue报错:Component name “xxx” should always be multi-word vue/multi-word-component

问题&#xff1a;搭建脚手架时报错&#xff0c;具体错误如下&#xff1a; ERROR in [eslint] E:\personalProject\VueProjects\vueproject2\src\components\Student.vue10:14 error Component name "Student" should always be multi-word vue/multi-word-compon…

【分布式数据仓库Hive】常见问题及解决办法

目录 一、启动hive时发现log4j版本和hadoop的版本有冲突 解决办法&#xff1a;删除hive下高版本的slf4j 二、启动hive报错 Exception in thread "main" java.lang.NoSuchMethodError:com.google.common.base.Preconditions.checkArgument(ZLjava/lang/Object;)V …

Elasticsearch (1):ES基本概念和原理简单介绍

Elasticsearch&#xff08;简称 ES&#xff09;是一款基于 Apache Lucene 的分布式搜索和分析引擎。随着业务的发展&#xff0c;系统中的数据量不断增长&#xff0c;传统的关系型数据库在处理大量模糊查询时效率低下。因此&#xff0c;ES 作为一种高效、灵活和可扩展的全文检索…

分别使用netty和apache.plc4x测试读取modbus协议的设备信号

记录一下常见的工业协议数据读取方法 目录 前言Modbus协议说明Netty 读取测试使用plc4x 读取测试结束语 前言 Modbus 是一种通讯协议&#xff0c;用于在工业控制系统中进行数据通信和控制。Modbus 协议主要分为两种常用的变体&#xff1a;Modbus RTU 和 Modbus TCP/IP Modbus …

嵌入式Linux之Uboot简介和移植

uboot简介 uboot 的全称是 Universal Boot Loader&#xff0c;uboot 是一个遵循 GPL 协议的开源软件&#xff0c;uboot是一个裸机代码&#xff0c;可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高级功能。 也就是说&#xff0c;可以在没有系统的情况…

苹果手机收不到短信怎么恢复?90%的人都在这么做

在使用苹果手机的过程中&#xff0c;有时候会遇到无法接收短信的问题。这不仅影响正常的沟通&#xff0c;还可能错过重要的通知和验证码。那么&#xff0c;手机收不到短信怎么恢复呢&#xff1f;别担心&#xff0c;90%的人都在使用这些简单而有效的方法来解决这一问题。 本文将…

Halcon支持向量机

一 支持向量机 1 支持向量机介绍&#xff1a; 支持向量机(Support Vector Machine&#xff0c;SVM)是Corinna Cortes和Vapnik于1995年首先提出的&#xff0c;它在解决小样本、非线性及高维模式识别表现出许多特有的优势。 2 支持向量机原理: 在n维空间中找到一个分类超平面…