当前位置: 首页 > news >正文

C语言进阶(八)—— 链表

1. 链表基本概念

1.1 什么是链表

  • 链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序存储)。

  • 数据域用来存储数据,指针域用于建立与下一个结点的联系。

  • 建立链表时无需预先知道数据总量的,可以随机的分配空间,可以高效的在链表中的任意位置实时插入或删除数据。

  • 链表的开销,主要是访问顺序性和组织链的空间损失。

数组和链表的区别:

数组:一次性分配一块连续的存储区域。

优点:随机访问元素效率高

缺点:1) 需要分配一块连续的存储区域(很大区域,有可能分配失败)

2) 删除和插入某个元素效率低

链表:无需一次性分配一块连续的存储区域,只需分配n块节点存储区域,通过指针建立关系。

优点:1) 不需要一块连续的存储区域

2) 删除和插入某个元素效率高

缺点:随机访问元素效率低

1.2 有关结构体的自身引用

问题1:请问结构体可以嵌套本类型的结构体变量吗?

问题2:请问结构体可以嵌套本类型的结构体指针变量吗?

typedef struct _STUDENT{char name[64];int age;
}Student;typedef struct _TEACHER{char name[64];Student stu; //结构体可以嵌套其他类型的结构体//Teacher stu;//struct _TEACHER teacher; //此时Teacher类型的成员还没有确定,编译器无法分配内存struct _TEACHER* teacher; //不论什么类型的指针,都只占4个字节,编译器可确定内存分配
}Teacher;
  • 结构体可以嵌套另外一个结构体的任何类型变量;

  • 结构体嵌套本结构体普通变量(不可以)。本结构体的类型大小无法确定,类型本质:固定大小内存块别名;

  • 结构体嵌套本结构体指针变量(可以), 指针变量的空间能确定,32位, 4字节、 64位, 8字节;

1.3 链表节点

大家思考一下,我们说链表是由一系列的节点组成,那么如何表示一个包含了数据域和指针域的节点呢?

链表的节点类型实际上是结构体变量,此结构体包含数据域和指针域:

  • 数据域用来存储数据;

  • 指针域用于建立与下一个结点的联系,当此节点为尾节点时,指针域的值为NULL

typedef struct Node 
{//数据域int id;char name[50];//指针域struct Node *next;       
}Node;

1.4 链表的分类

链表分为:静态链表和动态链表

静态链表和动态链表是线性表链式存储结构的两种不同的表示方式:

  • 所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“静态链表”。

  • 所谓动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。

1.4.1 静态链表

typedef struct Stu
{int id;    //数据域char name[100];struct Stu *next; //指针域
}Stu;void test()
{//初始化三个结构体变量Stu s1 = { 1, "yuri", NULL };Stu s2 = { 2, "lily", NULL };Stu s3 = { 3, "lilei", NULL };s1.next = &s2; //s1的next指针指向s2s2.next = &s3;s3.next = NULL; //尾结点Stu *p = &s1;while (p != NULL){printf("id = %d, name = %s\n", p->id, p->name);//结点往后移动一位p = p->next; }
}

1.4.2 动态链表

typedef struct Stu{int id;    //数据域char name[100];struct Stu *next; //指针域
}Stu;void test(){//动态分配3个节点Stu *s1 = (Stu *)malloc(sizeof(Stu));s1->id = 1;strcpy(s1->name, "yuri");Stu *s2 = (Stu *)malloc(sizeof(Stu));s2->id = 2;strcpy(s2->name, "lily");Stu *s3 = (Stu *)malloc(sizeof(Stu));s3->id = 3;strcpy(s3->name, "lilei");//建立节点的关系s1->next = s2; //s1的next指针指向s2s2->next = s3;s3->next = NULL; //尾结点//遍历节点Stu *p = s1;while (p != NULL){printf("id = %d, name = %s\n", p->id, p->name);//结点往后移动一位p = p->next; }//释放节点空间p = s1;Stu *tmp = NULL;while (p != NULL){tmp = p;p = p->next;free(tmp);tmp = NULL;}
}

1.4.3 带头和不带头链表

  • 带头链表:固定一个节点作为头结点(数据域不保存有效数据),起一个标志位的作用,以后不管链表节点如果改变,此头结点固定不变

  • 不带头链表:头结点不固定,根据实际需要变换头结点(如在原来头结点前插入新节点,然后,新节点重新作为链表的头结点)。

1.4.4 单向链表、双向链表、循环链表

单向链表:

双向链表:

循环链表:

2. 链表基本操作

2.1 创建链表

使用结构体定义节点类型:

typedef struct _LINKNODE
{int id; //数据域struct _LINKNODE* next; //指针域
}link_node;

编写函数:link_node* init_linklist()

建立带有头结点的单向链表,循环创建结点,结点数据域中的数值从键盘输入,以 -1 作为输入结束标志,链表的头结点地址由函数值返回。

typedef struct _LINKNODE{int data;struct _LINKNODE* next;
}link_node;link_node* init_linklist(){//创建头结点指针link_node* head = NULL;//给头结点分配内存head = (link_node*)malloc(sizeof(link_node));if (head == NULL){return NULL;}head->data = -1;head->next = NULL;//保存当前节点link_node* p_current = head;int data = -1;//循环向链表中插入节点while (1){printf("please input data:\n");scanf("%d",&data);//如果输入-1,则退出循环if (data == -1){break;}//给新节点分配内存link_node* newnode = (link_node*)malloc(sizeof(link_node));if (newnode == NULL){break;}//给节点赋值newnode->data = data;newnode->next = NULL;//新节点入链表,也就是将节点插入到最后一个节点的下一个位置p_current->next = newnode;//更新辅助指针p_currentp_current = newnode;}return head;
}

2.2 遍历链表

编写函数:void foreach_linklist(link_node* head)

顺序输出单向链表各项结点数据域中的内容:

//遍历链表
void foreach_linklist(link_node* head){if (head == NULL){return;}//赋值指针变量link_node* p_current = head->next;while (p_current != NULL){printf("%d ",p_current->data);p_current = p_current->next;}printf("\n");
}

2.3 插入节点

编写函数: void insert_linklist(link_node* head,int val,int data)

在指定值后面插入数据data,如果值val不存在,则在尾部插入:li

//在值val前插入节点
void insert_linklist(link_node* head, int val, int data){if (head == NULL){return;}//两个辅助指针link_node* p_prev = head;link_node* p_current = p_prev->next;while (p_current != NULL){if (p_current->data == val){break;}//两个辅助指针向后p_prev = p_current;p_current = p_prev->next;}//如果p_current为NULL,说明不存在值为val的节点//if (p_current == NULL){//    printf("不存在值为%d的节点!\n",val);//    return;//}//创建新的节点link_node* newnode = (link_node*)malloc(sizeof(link_node));newnode->data = data;newnode->next = NULL;//新节点入链表newnode->next = p_current;p_prev->next = newnode;
}

2.4 删除节点

编写函数: void remove_linklist(link_node* head,int val)

删除第一个值为val的结点:

//删除值为val的节点
void remove_linklist(link_node* head,int val){if (head == NULL){return;}//辅助指针link_node* p_prev = head;link_node* p_current = p_prev->next;//查找值为val的节点while (p_current != NULL){if (p_current->data == val){break;}p_prev = p_current;p_current = p_prev->next;}//如果p_current为NULL,表示没有找到if (p_current == NULL){return;}//删除当前节点: 重新建立待删除节点(p_current)的前驱后继节点关系p_prev->next = p_current->next;//释放待删除节点的内存free(p_current);
}

2.5 销毁链表

编写函数: void destroy_linklist(link_node* head)

销毁链表,释放所有节点的空间:

//销毁链表
void destroy_linklist(link_node* head){if (head == NULL){return;}//赋值指针link_node* p_current = head;while (p_current != NULL){//缓存当前节点下一个节点link_node* p_next = p_current->next;free(p_current);p_current = p_next;}
}

2.6 反转链表

编写函数: void reverse_linklist(link_node* head)

反转链表通过3个辅助指针变量实现链表的翻转:

void reverse_linklist(link_node* head){if (head == NULL){return;}//辅助指针link_node* p_prev = NULL;link_node* p_current = head->next;link_node* p_next = NULL;while (p_current != NULL){p_next = p_current->next;//更改指针指向p_current->next = p_prev;//移动辅助指针p_prev = p_current;p_current = p_next;}//更新头结点head->next = p_prev;
}

2.7 统计链表长度

编写函数: int size_linklist(link_node* head)

int size_linklist(link_node* head){if (head == NULL){return -1;}//临时指针变量执行第一个真实数据的结点link_node* p_current = head->next;//记录结点个数int num = 0;while (p_current != null) {num++;p_current = p_current->next;}return num;
}

相关文章:

C语言进阶(八)—— 链表

1. 链表基本概念1.1 什么是链表链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序存储)。数据域用来存储数据,指针域用于建立与下一个结点的…...

手工测试用例就是自动化测试脚本——使用ruby 1.9新特性进行自动化脚本的编写

昨天因为要装watir-webdriver的原因将用了快一年的ruby1.8.6升级到了1.9。由于1.9是原生支持unicode编码,所以我们可以使用中文进行自动化脚本的编写工作。 做了简单的封装后,我们可以实现如下的自动化测试代码。请注意,这些代码是可以正确运…...

RockerMQ简介和单节点部署

目录一、RockerMQ简介二、Linux中单节点部署1、准备工作2、下载和解压3、修改初始内存4、启动5、查看进程6、发送接收消息测试7、关闭三、控制台的安装与启动(可视化页面)1、修改配置(1)修改端口号(2)指定RocketMQ的name server地…...

SFP光纤笼子 别称 作用 性能要点 工程要素

Hqst盈盛电子导读:2023年,Hqst盈盛电子于下属五金部开发生产SFP光纤连接器笼子等系列产品,所有产品生产及性标准都将参照连接器产品常用测试标准EIA-364-C等标准,以下为我司常规SFP光纤连接器基本性能要求SFP光纤笼子别称&#xf…...

[HarekazeCTF2019]Easy Notes

知识点:session 反序列化,代码审计代码分析 flag.php 中有个 is_admin 函数的判断。 在 lib.php 中有 is_admin 函数,需要 session[admin] 为 true,或者通过文件读取的方式。 在 index.php 中的 include 并不能使用伪协议读取 …...

Java学习-IO流-字符流-FileReader

Java学习-IO流-字符流-FileReader 字符流 字节流 字符集 输入流:默认一次读一个字节,遇到中文时一次读多个字节 输出流:底层把数据按照指定编码方式编码,变成字节写入文件 使用场景:纯文本文件读写 // …...

python攻陷米哈游《元神》数据?详情请看文章。。

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 《原神》是由米哈游自研的一款全新开放世界冒险RPG。 里面拥有许多丰富得角色,让玩家为之着迷~ 今天,我们就来用python探索一下原神游戏角色信息! 标题大家看看就好了哈~&#xff08…...

【unity细节】基于unity子对象(如相机)为什么无法进行z轴的拖拽移动和z轴自动归位的问题

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:unity细节和bug ⭐基于unity子对象为什么无法进行z轴的拖拽移动和z轴自动归位⭐ 文章目录⭐基于u…...

如何维护固态继电器?

固态继电器是SSR的简称,是由微电子电路、分立电子器件和电力电子功率器件组成的非接触式开关。隔离装置用于实现控制端子与负载终端之间的隔离。固态继电器的输入端使用微小的控制信号直接驱动大电流负载。那么,如何保养固态继电器呢? 在为小…...

Sprng依赖注入(三):构造方法注入是如何工作的?

前言这是Spring依赖注入系列的第三篇,前两篇主要分析了Spring bean依赖属性注入的两种方式,是字段注入和setter方法注入,单独比较这两种方式,会发现其过程和工作原理非常类似,那么构造方法注入会不会也和前两种比较类似…...

「1」指针进阶——详解

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 🐰指针的回顾 🐰字符指针 🐰指针数组 🌸模…...

JS语法让人困惑的点 “==与===”

在JS中有很多神奇的语法,非常让人困惑,我们就先一一道来,相信你在开发中或多或少都踩过这些坑,或者让人无法理解。 今天我们就来说下【】和【】 这题对于很多没有系统学过前端开发的技术人员来说,算个重点&#xff0c…...

《狂飙》壁纸大嫂如此惊艳,做成日历壁纸天天看

兄弟们,今年的反腐大剧狂飙都有看吗 ? 话说,名字虽然叫狂飙,但是全剧只有有田一个人在狂飙! 当然,有田虽然亮眼,但是毕竟是个糟老头子,正经人谁看有田啊,当然是看大嫂了…...

手机照片删除了怎么恢复

手机照片删除了怎么恢复?喜欢拍照的小伙伴,都会不定期删除手机上的照片,因为这些爱拍照的人,手机中会存储着很多照片,删除照片是必然的,但在手机删除照片时,如果是一张一张删除太麻烦了,就直接…...

maven pom.xml 依赖的scope属性

maven pom.xml 依赖的scope属性 compile 适用范围 编译期、测试期、运行期 作用 从中央仓库拉取依赖到本地,并编译 打包到结果包中 runtime 适用范围 测试期、运行期 作用 runtime 用在 Class.forName(“com.mysql.jdbc.Driver”) 时,compile 编…...

git 的使用方法 (下 - 远程仓库和图形化)

目录前言:一、什么是协同开发二、Gitee 使用协同开发1. 首先注册一个码云账号2. 新建一个仓库3. 根据下图把新建仓库设置为开源4. 在远端合并分支的方法5. 链接 git 远程6. 提交(同步)远程7. 远程拉取至本地8. 远程分支三、git 图形化的使用1…...

Java基础:拼图小游戏

涉及到的知识: 1.图形用户接口GUI(Graphical User Interface)用图形化的方式显示操作界面 两个体系: AWT包和Swing包 2.界面会用到JFrame类 3.界面中的菜单会用到JMenuBar, JMenu, JMenuItem 4.添加图片 在设置完JLabel的location之后还需要获得展示内容的窗体, 通过setLay…...

一个跟蘑菇结缘的企业老板

记得那是一个很久以前的一家公司了董事长办公室里中的大型盆栽里面长了一个蘑菇董事长认为是祥瑞每天都会浇水后来一个新来的保洁阿姨以为杂草啥的给他掰掉扔垃圾桶了董事长第二天来浇水的时候发现没了就问谁动了他的蘑菇问道之后就跑到楼道大垃圾桶那里把蘑菇找回来种在花盆里…...

【Leetcode 剑指Offer】第 4 天 查找算法(简单)

查找剑指 Offer 03. 数组中重复的数字剑指 Offer 53 - I. 在排序数组中查找数字 I二分法题目链接剑指 Offer 03. 数组中重复的数字 题:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数…...

Jenkins利用docker部署vue项目

Jenkins利用docker部署vue项目一、环境准备1、安装docker2、安装nodejs3、安装cnpm与配置淘宝镜像4、jenkins安装nodejs插件二、jenkins以vue项目1、全局参数配置2、源码配置3、构建环境4、构建三、构建项目四、访问一、环境准备 本次jenkins与部署vue项目在同一台机器&#x…...

【Linux】如何将ntfs硬盘挂载到home目录下并具有读写权限

步骤1. 查看当前挂载的硬盘及其挂载点2. 查看需要挂载到home下的磁盘类型信息3. 在home下新建一个空的文件夹作为该磁盘的新挂载点4. 以ntfs类型的硬盘为例,使用mount命令进行挂载5. 问题1:进程占用了磁盘6. 问题2:磁盘权限为只读的7. 永久挂…...

拖拽删除元素、拖拽排序、拖拽预览图片和拖拽移动元素

介绍 HTML5 提供了专门的拖拽与拖放的 API,目前各浏览器都已支持,包括 IE。HTML 拖放(Drag and Drop)接口使应用程序能够在浏览器中使用拖放功能。例如,用户可使用鼠标选择可拖拽(draggable)元素…...

yarn的global安装命令不生效

问题 yarn全局安装某个依赖完成之后,但依赖没有生效,一般有两种情况导致的。 解决思路 1.yarn命令问题 yarn在全局安装某个依赖时,global要紧接在yarn之后,然后才是add yarn global add xxxx如果出现global在add之后&#xff…...

如何发布自己的npm包?

1 文件组成 package.json文件components文件css样式文件index.js文件 2 package.json配置 description:描述title:题目keywords:搜索关键词typings:指定TypeScript的入口文件main:加载的入口文件module:…...

达梦数据库 闪回查询

当用户操作不慎导致错误的删改数据时,非常希望有一种简单快捷的方式可以恢复数据。闪回技术,就是为了用户可以迅速处理这种数据逻辑损坏的情况而产生的。 闪回技术主要是通过回滚段存储的 UNDO 记录来完成历史记录的还原。如果提交了,还没有…...

java基础学习 day44(多态的优点和劣势)

1. 多态的优势 在多态形式下,右边对象可以实现解耦合(即之后的代码与右边的子类对象不绑定,在更改子类对象后,之后的代码仍可以使用),便于扩展和维护在定义方法的时候,使用父类型作为参数&…...

Guna UI WinForms 2.0.4.4 Crack

Guna.UI2 WinForms is the suite for creating groundbreaking desktop app UI. It is for developers targeting the .NET Windows Forms platform. 50 多个 UI 控件 具有广泛功能的综合组件可帮助您开发任何东西。 无尽的定制 只需拖放即可创建视觉效果命令和体验。 出色的…...

零售航母沃尔玛公布业绩:喜忧参半

2月21日美股盘前,零售巨无霸沃尔玛公布了截至1月的2023财年第四季度业绩报告。财报中不乏可圈可点之处,但是利润迎来六年首降,新财年的利润指引要也比预期低很多,可以说喜忧参半。 一、Q4业绩可圈可点 营收方面:在本…...

Python学习笔记丨while、for、if循环结构基础知识与易错点

Python流程控制 本篇笔记的主要内容是:条件控制和循环控制,包括if语句、while语句、for语句等。 Python条件控制 if (m : 1) > 0: # :是海象运算符,用于在函数内部为变量赋值 print("ok")ok 通过if语句来判断条件是否成立&am…...

【ROS学习笔记1】ROS快速体验输出Hello World

【ROS学习笔记1】ROS快速体验输出Hello World 文章目录【ROS学习笔记1】ROS快速体验输出Hello World1.1 ROS快速体验1.1.1 Hello World快速实现简介1.1.2 Hello World的C实现1.1.3 Hello World的Python实现写在前面,本系列笔记参考的是AutoLabor的教程,具…...