C和C++(list)的链表初步

链表是构建其他复杂数据结构的基础,如栈、队列、图和哈希表等。通过对链表进行适当的扩展和修改,可以实现这些数据结构的功能。想学算法,数据结构,不会链表是万万不行的。这篇笔记是一名小白在学习时整理的。
C语言
链表部分
链表的定义
这里用原始的方法定义了一个含有3个节点的链表,并用原始的办法输出。
输出时,一种用“ -> ”,另一种只用“ . ”
#include <stdio.h>
#include <stdlib.h>typedef struct Node {int date;struct Node* next;
}Node;int main() {Node node01 = { 11,NULL };Node node02 = { 22,NULL };Node node03 = { 33,NULL };node01.next = &node02;node02.next = &node03;printf("%d\n", node01.date);printf("%d\n", node01.next->date);printf("%d\n", (*(node01.next)).date);printf("%d\n", node01.next->next->date);printf("%d\n", (*(*(node01.next)).next).date);system("pause");
}
链表的遍历
循环遍历
以循环遍历的方式输出链表的数据
这里需要用到指针
#include <stdio.h>
#include <stdlib.h>typedef struct Node {int date;struct Node* next;
}Node;int main() {Node node01 = { 11,NULL };Node node02 = { 22,NULL };Node node03 = { 33,NULL };node01.next = &node02;node02.next = &node03;Node* point = &node01;while (point != NULL) {printf("%d\n", point->date);point = point->next;}system("pause");
}
递归遍历
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int date;struct Node* next;
}Node;void printList(Node* point);
int main() {Node node01 = { 11,NULL };Node node02 = { 22,NULL };Node node03 = { 33,NULL };node01.next = &node02;node02.next = &node03;Node* point = &node01;printList(point);system("pause");
}void printList(Node* point) {if(point != NULL){printf("%d\n", point->date);printList(point->next);}
}
链表的插入
后插法
将一个新节点添加在date为22的节点之后
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int date;struct Node* next;
}Node;
void printList(Node* point);
void insertListBehind(Node* point, int id, Node* newnode);int main() {Node node01 = { 11,NULL };Node node02 = { 22,NULL };Node node03 = { 33,NULL };node01.next = &node02;node02.next = &node03;Node* point = &node01;Node newnode = { 23,NULL };insertListBehind(point, 22, &newnode);printList(point);system("pause");
}void insertListBehind(Node* point, int id,Node*newnode) {while (point != NULL) {if (point->date == id) {newnode->next = point->next;point->next = newnode;}point = point->next;}
}void printList(Node* point) {if(point != NULL){printf("%d\n", point->date);printList(point->next);}
}
前插法
比后插难一点点,要用到头指针。
要考虑到前插到第一个节点的情况(单独考虑),我们可以用后插法来实现前插法。
即,如果前插到data为22的前面,我们可以转化为后插到data为22的节点的前一个节点的后面。
用到了二级指针
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;
} Node;
void printList(Node* head);
void insertListFront(Node** head, int id, Node* newnode);int main() {Node node01 = { 11, NULL };Node node02 = { 22, NULL };Node node03 = { 33, NULL };node01.next = &node02;node02.next = &node03;Node* head = &node01;Node newnode = { 12, NULL };insertListFront(&head, 22, &newnode);printList(head);system("pause");return 0;
}
void insertListFront(Node** head, int id, Node* newnode) {Node* point = *head;if (point->data == id) {newnode->next = point;*head = newnode;}else {while (point->next != NULL) {if (point->next->data == id) {newnode->next = point->next;point->next = newnode;break;}point = point->next;}}
}void printList(Node* head) {Node* point = head;while (point != NULL) {printf("%d\n", point->data);point = point->next;}
}
链表的删除
可以参照前插法的思路,几乎一样的
这里删除data为22的那个节点。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;
} Node;
void printList(Node* head);
void deleteListFront(Node** head, int id);int main() {Node node01 = { 11, NULL };Node node02 = { 22, NULL };Node node03 = { 33, NULL };node01.next = &node02;node02.next = &node03;Node* head = &node01;deleteListFront(&head, 22);printList(head);system("pause");return 0;
}
void deleteListFront(Node** head, int id) {Node* point = *head;if (point->data == id) {*head = point->next;}else {while (point->next != NULL) {if (point->next->data == id) {point->next = point->next->next;break;}point = point->next;}}
}void printList(Node* head) {Node* point = head;while (point != NULL) {printf("%d\n", point->data);point = point->next;}
}
注意:这里的删除仅仅是断开了链表与该块数据的链接,并不是真正的删除。
真正的删除在下面《动态链表节点的删除》
动态链表
链表的动态添加
于链表前端添加节点
类似前插法,但是用malloc,实际上更简单一点。
我们可以利用头指针来添加
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;
}Node;
void addNodeFront(Node** head, int data);
void printList(Node* head);int main() {Node* head = NULL;addNodeFront(&head, 111);addNodeFront(&head, 222);addNodeFront(&head, 333);printList(head);
}
void addNodeFront(Node** head, int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->next = NULL;if (*head == NULL) {*head = newNode;}else {newNode->next = *head;*head = newNode;}
}void printList(Node* head) {Node* point = head;while (point != NULL) {printf("%d\n", point->data);point = point->next;}
}
于链表后端添加节点
比前加难一点。因为头指针只指头,所以要找到尾指针。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;
}Node;
void addNodeBehind(Node** head, int data);
void printList(Node* head);int main() {Node* head = NULL;addNodeBehind(&head, 111);addNodeBehind(&head, 222);addNodeBehind(&head, 333);printList(head);
}
void addNodeBehind(Node** head, int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->next = NULL;if (*head == NULL) {*head = newNode;}else {Node* point = *head;while(point->next != NULL) {point = point->next;}point->next = newNode;}
}void printList(Node* head) {Node* point = head;while (point != NULL) {printf("%d\n", point->data);point = point->next;}
}
动态链表删除节点
由于C语言不会像Java那样有垃圾处理器,断开连接后内存不会自动释放。
我们可以使用 free() 来实现。free掉从链表上取下的节点。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;
}Node;
void deleteNode(Node** head, int id);
void addNodeBehind(Node** head, int data);
void printList(Node* head);int main() {Node* head = NULL;addNodeBehind(&head, 111);addNodeBehind(&head, 222);addNodeBehind(&head, 333);deleteNode(&head, 222);printList(head);
}void deleteNode(Node** head, int id) {Node* point = *head;if (point->data == id) {*head = point->next;free(point);}else {while (point->next != NULL) {if (point->next->data == id) {Node* deletetemp = point->next;point->next = deletetemp->next;free(deletetemp);}point = point->next;}}
}void addNodeBehind(Node** head, int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->next = NULL;if (*head == NULL) {*head = newNode;}else {Node* point = *head;while(point->next != NULL) {point = point->next;}point->next = newNode;}
}
void printList(Node* head) {Node* point = head;while (point != NULL) {printf("%d\n", point->data);point = point->next;}
}
只需要在原来删除节点的基础上,标记出要删除的那个节点,在断开链接后,再free掉它。
循环列表
之前学习研究的链表都是条状的,非循环的连表,有头有尾。循环列表是环状的哦。
循环列表的插入及遍历
如果循环列表为空,即插入的节点为第一个节点,该节点的next应指向自身。若不为空,先找到原列表最后一个,添加后,让新的末端指向头。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;
}Node;
void addNodeBehind(Node** head, int data);
void printList(Node* head);int main() {Node* head = NULL;addNodeBehind(&head, 111);addNodeBehind(&head, 222);addNodeBehind(&head, 333);printList(head);
}void addNodeBehind(Node** head, int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->next = NULL;if (*head == NULL) {*head = newNode;}else {Node* point = *head;while(point->next != *head) {point = point->next;}point->next = newNode;}newNode->next = *head;
}
void printList(Node* head) {Node* point = head;do{printf("%d\n", point->data);point = point->next;} while (point != head);
}
注意:因为链表的结束标志不再是节点的next指向为空,而是指向为头结点,所以遍历时应做出修改。因为point一开始就指向头节点,结束标志也是头节点,所以我使用do-while循环。
双向链表
大多为环装
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;struct Node* next;struct Node* prev;
}Node;
void addNode(Node**head,int data);
void printList(Node* head);int main() {Node* head = NULL;addNode(&head, 111);addNode(&head, 222);addNode(&head, 333);printList(head);return 0;
}void addNode(Node** head, int data) {Node* newnode = (Node*)malloc(sizeof(Node));newnode->data = data;newnode->next = newnode;newnode->prev = newnode;if (*head == NULL) {*head = newnode;}else {Node* first = *head;Node* last = first->prev;last->next = newnode;newnode->prev = last;newnode->next = first;first->prev = newnode;}
}void printList(Node* head) {Node* point = head;do {printf("%d\n", point->data);point = point->next;}while (point != head);
}
相比之下,双向链表插入等操作时,要断开2个链接,建立2个链接。但是不必使用循环。因为可以直接通过头找到尾。
C++语言
C++ List的基础知识
和<vector>的用法很像。
但是不能通过索引(索引的实质是地址的偏移量)来查找,因为链表的内存不是连续的,不像数组那样。不能随机查找
一些list函数,关键字等将在代码中进行注释
list的的声明
#include <iostream>
#include <list>int main() {std::list<int> list1; // 创建一个空的链表std::list<int> list2(3, 100); // 创建一个包含3个值为100的元素的链表std::list<int> list3(l2); // 创建一个l2的副本return 0;
}
list元素的访问和修改
访问函数
front():返回链表中第一个元素的引用back():返回链表中最后一个元素的引用
修改函数
push_front():在链表头部插入一个元素pop_front():移除链表头部的元素push_back():在链表尾部插入一个元素pop_back():移除链表尾部的元素insert():在指定位置插入一个元素- remove():删除指定的元素
clear():移除链表中的所有元素- unique():除重
#include<iostream>
#include<list>
using namespace std;int main() {list<int> List;List.push_back(111);List.push_back(222);List.push_back(333);List.push_front(444);List.pop_back();List.pop_front();for (auto i : List) {cout << i << " ";}return 0;
}
输出结果为111 222
list的基础操作
操作函数
merge():将另一个有序链表other合并到当前链表中,合并后链表仍然有序sort():对链表中的元素进行排序reverse():反转链表中元素的顺序
容量函数
empty():判断链表是否为空size():返回链表中元素的数量
#include<iostream>
#include<list>
using namespace std;int main() {list<int> List;List.push_back(111);List.push_back(222);List.push_back(333);cout << "List size: " << List.size() << endl;while (!List.empty()) {cout << List.front() << endl;List.pop_front();}cout << List.empty() << endl;cout << "List size: " << List.size() << endl;return 0;
}
C++List的迭代器
迭代器相关函数
begin():返回指向链表第一个元素的迭代器end():返回指向链表末尾(最后一个元素之后)的迭代器rbegin():返回指向链表最后一个元素的反向迭代器rend():返回指向链表开头(第一个元素之前)的反向迭代器
begin()和end()的使用示例
#include<iostream>
#include<list>
using namespace std;int main() {list<int> List;List.push_back(111);List.push_back(222);List.push_back(333);cout << *List.begin() << endl;cout << *--List.end() << endl;return 0;
}
注意:.end并不指向最后一个节点,其指向为空,用--list.end可以获得链表List的最后的节点的地址。
List迭代器的使用
list< >::iterator __
Vlist<int>::iterator it = List.begin();
创建一个名字叫 it 的迭代器,他指向名字叫 List 的链表的第一个节点
advance( , )
advance(it, 2);
将名字叫 it 的迭代器正向移动2个节点。
这里是输出第3到第4的节点的数据
#include<iostream>
#include<list>
using namespace std;int main() {list<int> List;List.push_back(000);List.push_back(111);List.push_back(222);List.push_back(333);List.push_back(444);list<int>::iterator it = List.begin();advance(it, 2);for (int i = 0; i < 2; i++) {cout << *it << endl;it++;}return 0;
}
相关文章:
C和C++(list)的链表初步
链表是构建其他复杂数据结构的基础,如栈、队列、图和哈希表等。通过对链表进行适当的扩展和修改,可以实现这些数据结构的功能。想学算法,数据结构,不会链表是万万不行的。这篇笔记是一名小白在学习时整理的。 C语言 链表部分 …...
深入浅出 TypeScript 泛型:类型安全的艺术与实践
文章目录 一、泛型的核心概念1.1 类型参数:代码中的类型变量1.2 类型推断:让代码保持简洁 二、泛型的四大应用场景2.1 泛型函数:打造通用工具库2.2 泛型接口:定义灵活的数据结构2.3 泛型类:构建类型安全的容器2.4 泛型…...
【KWDB创作者计划】_KaiwuDB 2.1.0 单节点裸机部署
大家好,这里是 DBA学习之路,专注于提升数据库运维效率。 目录 前言KWDB 介绍安装准备环境信息配置要求操作系统软件依赖端口要求安装包下载 部署 KWDB简单实用连接数据库创建数据库创建用户创建时序表 前言 今天无意间在墨天轮看到一个征文活动 征文大赛…...
洛谷题单3-P5720 【深基4.例4】一尺之棰-python-流程图重构
题目描述 《庄子》中说到,“一尺之棰,日取其半,万世不竭”。第一天有一根长度为 a a a 的木棍,从第二天开始,每天都要将这根木棍锯掉一半(每次除 2 2 2,向下取整)。第几天的时候木…...
前端快速入门学习3——CSS介绍与选择器
1.概述 CSS全名是cascading style sheets,中文名层叠样式表。 用于定义网页样式和布局的样式表语言。 通过 CSS,你可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式,从而实现更精确的页面设计。 HTML与CSS的关系:HTML相当…...
Redash:一个开源的数据查询与可视化工具
Redash 是一款免费开源的数据可视化与协作工具,可以帮助用户快速连接数据源、编写查询、生成图表并构建交互式仪表盘。它简化了数据探索和共享的过程,尤其适合需要团队协作的数据分析场景。 数据源 Redash 支持各种 SQL、NoSQL、大数据和 API 数据源&am…...
嵌入式Linux驱动—— 1 GPIO配置
目录 1.GPIO操作 1.1 IO命名 1.2 GPIO 时钟使能(CCM) 1.3 IO 复用(IOMUXC) 1.4 IO 配置 1.5 GPIO 配置 1.GPIO操作 GPIO操作主要是以下流程: 使能某组GPIO模块(GPIO1、2、...)&#…...
[ICLR 2025]Biologically Plausible Brain Graph Transformer
论文网址:Biologically Plausible Brain Graph Transformer 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 …...
SpringBoot+MyBatis Plus+PageHelper+vue+mysql 实现用户信息增删改查功能
静态资源展示 (1)静态资源下载 (2)下载后文件放到resources/static 目录下 (3) main函数启动项目访问对应文件,http://127.0.0.1:8080/user-list.html 数据库添加表和数据 SET FOREIGN_KEY_CHECKS0;-- --------…...
企业常用Linux服务搭建
1.需要两台centos 7服务器,一台部署DNS服务器,另一台部署ftp和Samba服务器。 2. 部署DNS 服务器 #!/bin/bash# 更新系统 echo "更新系统..." sudo yum update -y# 安装 BIND 和相关工具 echo "安装 BIND 和相关工具..." sudo y…...
Qwen-7B-Chat 本地化部署使用
通义千问 简介 通义千问是阿里云推出的超大规模语言模型,以下是其优缺点: 优点 强大的基础能力:具备语义理解与抽取、闲聊、上下文对话、生成与创作、知识与百科、代码、逻辑与推理、计算、角色扮演等多种能力。可以续写小说、编写邮件、解…...
QGIS获取建筑矢量图-Able Software R2V
1.QGIS截图 说明:加载天地图矢量图层,然后进行截图。 2.Able Software R2V 说明:Able Software R2V 是一款将光栅图像(如扫描图纸、航拍照片)自动转换为矢量图形(如DXF格式)的软件&a…...
CSS:换行与不换行
一、CSS 不允许换行 在 CSS 中,有几种方法可以控制文本不换行: 1. 使用 white-space 属性 .no-wrap {white-space: nowrap; } white-space: nowrap; 会强制文本在一行显示,不换行。 2. 使用 overflow 和 text-overflow 通常与 white-sp…...
【MiniMind】不能全局用 `pip install --upgrade pip`
Q:WARNING: Running pip as the ‘root’ user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.…...
form实现pdf文件转换成jpg文件
说明: 我希望将pdf文件转换成jpg文件 请去下载并安装 Ghostscript,gs10050w64.exe 配置环境变量:D:\Program Files\gs\gs10.05.0\bin 本地pdf路径:C:\Users\wangrusheng\Documents\name.pdf 输出文件目录:C:\Users\wan…...
STM32单片机入门学习——第13节: [6-1] TIM定时中断
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.04 STM32开发板学习——第13节: [6-1] TIM定时中断 前言开发板说明引用解答和科普一…...
量子纠错码实战:从Shor码到表面码
引言:量子纠错的必要性 量子比特的脆弱性导致其易受退相干和噪声影响,单量子门错误率通常在10⁻~10⁻量级。量子纠错码(QEC)通过冗余编码测量校正的机制,将逻辑量子比特的错误率降低到可容忍水平。本文从首个量子纠错…...
【2】搭建k8s集群系列(二进制)之安装etcd数据库集群
一、etcd服务架构 Etcd 是一个分布式键值存储系统,Kubernetes 使用 Etcd 进行数据存储,所以先 准备一个 Etcd 数据库,为解决 Etcd 单点故障,应采用集群方式部署,这里使用 3 台组建集群,可容忍 1 台机器故障…...
Linux常用命令详解:从基础到进阶
目录 一、引言 二、文件处理相关命令 (一)grep指令 (二)zip/unzip指令 编辑 (三)tar指令 (四)find指令 三、系统管理相关命令 (一)shutdown指…...
【Docker】使用Docker快速部署n8n和unclecode/crawl4ai
Docker部署自动化工具n8n和crawl4ai详细教程 前言 本文将详细介绍如何使用 Docker 来部署和运行自动化工作流工具 n8n 以及 crawl4ai。这两个工具对于需要进行自动化工作流程的开发者来说都非常有用。 一、环境准备 在开始之前,请确保您的系统已经安装了&#x…...
数据库权限获取
1. into outfile(手写) 1.1. 利用条件 • web 目录具有写入权限,能够使用单引号 • 知道网站绝对路径(根目录,或则是根目录往下的目录都行) • secure_file_priv 没有具体值(在 mysql/my.ini 中查看) 1.2. secure_file_priv 介绍 secure_file_priv 是用来限制 loa…...
基于spring boot的外卖系统的设计与实现【如何写论文思路与真正写出论文】
目录 系统开发实现链接: 背景与分析: 背景(题目): 用户功能 配送员功能 管理员功能 分析: 过程(主体展示为主,部分功能不一一展示): 目录 论文前面…...
Kubernetes 存储 Downward API
1.介绍 1.提供容器元数据 比如我们 golang语言 我们说他会根据当前CPU的数量 以此去确认我们的进程 线程 和协程之间的关系 以此去释放我们当前CPU的更大的 这么一个并行任务的能力 但是这里会出现一个问题 容器它是把当前的应用 封装在我们固定的名称空间了 而且给它以特定的…...
使用ctags+nvim自动更新标签文件
ctags是一个强大的语言分析工具,可以分析多种语言并生成语法结构文件,通过这些文件可以快速进行函数跳转,但是这有一个缺点,就是每次在项目里更新了代码之类的比如新增了一个函数,都需要重新使用ctags -R .来重新更新标…...
RK3568 gpio模拟i2c 配置hym8563 RTC时钟
1、使用模拟i2c,确认使用的gpio未被占用,为gpio功能 以GPIO0_C6 GPIO0_C7为例,查看管脚的复用关系。 cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins2、使用内核模块i2c-gpio.c 内核make menuconfig 开启i2c_gpio支持 Device Drivers->I2C sup…...
HANA如何在存储过程里执行动态SQL
业务场景需求: 在HANA里如何实现动态的SQL控制,比如需要多个单据里,实现某个自定义字段不允许重复 一般的写法是需要在每个业务单据里加对应的存储过程控制,这样的话,需要在每个业务单据里进行控制,SQL维…...
01人工智能基础入门
一、AI应用场景和发展历程 1.1行业应用 1、deepdream图像生成、yolo目标检测 2、知识图谱、画风迁移 3、语音识别、计算机视觉 4、用户画像 5、百度人工智能布局 1.2发展历程 人工智能的发展经历了 3 个阶段: 1980年代是正式成形期,尚不具备影响力。 …...
嵌入式AI的本地化部署的好处
嵌入式AI本地化处理(即边缘计算)的核心优势在于将AI算力下沉至设备端,直接处理数据而非依赖云端,这种模式在多个维度上展现出显著价值: 一、数据隐私与安全性提升 1. 敏感数据本地存储 金融、医疗等涉及隐私的行业…...
进程和内存管理
目录 一.进程的基本信息 1.1进程的定义 1.2进程的特征 1.3进程的组成 1.4线程产生的背景 1.5线程的定义 1.6进程与线程的区别 1.7进程的类别 1.8进程的优先级 1.8.1进程优先级的概念 1.8.2PRI和NI 1.9僵尸进程 1.9.1僵尸进程的定义 1.9.2僵尸进程产生的原因 1.9…...
001 vue
https://cn.vuejs.org/ 文章目录 v-bindv-modelv-on修饰符条件渲染/控制:v-if v-show列表渲染 M:即Model,模型,包括数据和一些基本操作 V:即View,视图,页面渲染结果 VM:即View-Mode…...
