数据结构 -- 链表

[数据结构]链表之单链表 单链表是一种常见的线性数据结构,由若干个节点组成,每个节点包含两部分:数据元素和指向下一个节点的指针。每个节点只有一个指针,通常称为 next 指针,它指向该节点的后继节点。头节点是第一个节点前面额外添加的一个节点,它不包含数据元素,但是包含对第一个节点的指针。单链表可以表示任意长度的序列,可以动态地插入、删除节点,使其具有灵活性和高效性。但由于每个节点只有一个指针,因此单链表只能从前往后遍历,不能反向遍历。以下是单链表常用的操作:创建链表:创建一个空链表,可选是否带有头节点。 阅读详情

1.单链表的按位序插入(带头结点)

struct LNode {   //定义单链表节点类型
	ElemType data;   //每个节点存放一个数据元素
	struct LNode* next;   //指针指向下一个节点
};

struct LNode* p = (struct LNode*)malloc(sizeof(struct LNode));
//增加一个新的结点:在内存中申请一个结点所需空间,并用指针p指向这个结点

完整代码:
#include <iostream>
#include <cstdlib>
using namespace std;

using ElemType = int;

typedef struct LNode {
	ElemType data;
	struct LNode* next;
}LNode,*LinkList;
 

bool ListInsert(LinkList& L, int i, ElemType e) {
	if (i < 1) {
		return false;     //非法位序
	}
	LNode* p = L;     //p指向头结点
	int j = 0;        //j表示p的位序(头结点算0)
	 
	while (p != NULL && j < i - 1) {    // 找到第 i-1 个结点
		p = p->next;  
		j++;
	}
	if (p == NULL) {     // i 过大,链表不够长 
		return false;
	}

	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;                                 ----解释1

	return true;
}


/* 辅助:初始化一个空链表(只含头结点) */
void InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));
	L->next = nullptr;
}

//尾插法建表,方便测试
void CreateListTail(LinkList& L, int n) {
	InitList(L);
	LNode* r = L;
	cout << "请依次输入" << n << "个元素:";
	for (int i = 0;i < n;i++) {
		ElemType x;
		cin >> x;
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = NULL;
		r -> next = s;
		r = s;
	}
}                                               ----解释2


//打印链表
void PrintList(LinkList L) {
	LNode* p = L->next;
	cout << "当前链表为:";
	while (p) {
		cout << p->data << (p->next ? "->" : "");
		p = p->next;
	}
	cout << endl;
}                                              ----解释3


/* 测试 */
int main() {
	LinkList L;
	InitList(L);          // 空链表

	/* 手动插入几个元素 */
	ListInsert(L, 1, 10); // 第 1 位插入 10
	ListInsert(L, 2, 20); // 第 2 位插入 20
	ListInsert(L, 1, 5);  // 再插到最前面
	PrintList(L);         // 应输出:5 -> 10 -> 20

	/* 交互式测试 */
	int pos, val;
	cout << "请输入要插入的位序 和 元素值(位序<1结束):";
	while (cin >> pos >> val) {
		if (pos < 1) break;
		if (ListInsert(L, pos, val))
			PrintList(L);
		else
			cout << "插入失败,位序非法或内存不足!" << endl;
	}

	 
	return 0;
}
解释1:
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;

这四句就是“把一个新生成结点 `s` 插到 `p` 结点后面”的核心四步,逐行解释如下:

1. `LNode* s = (LNode*)malloc(sizeof(LNode));`

   在内存堆里申请一块能装一个 `LNode` 的空间,并把这块空间的首地址赋给指针 `s`。

   通俗讲:造出一个“空结点”,让 `s` 指向它。

2. `s->data = e;`

   把要插入的元素值 `e` 放进这个新结点的数据域。

3. `s->next = p->next;`

   让新结点 `s` 的指针域先指向 `p` 当前的后继结点。

   换句话说:先把 `p` 原来的“下一跳”借过来,变成 `s` 的“下一跳”,这样后面那条链就不会丢。

4. `p->next = s;`

   最后把 `p` 的指针域改成指向 `s`,完成“插队”——从此 `p` 后面不再是旧结点,而是新结点 `s`。

画一条链更直观:

插入前:

… → p →  A  → …

插入后:

… → p →  s  →  A  → …

步骤 3、4 的顺序不能反,如果先执行 `p->next = s` 再执行 `s->next = p->next`,就会把 `A` 的地址丢掉,造成断链。

解释2:
//尾插法建表,方便测试
void CreateListTail(LinkList& L, int n) {
	InitList(L);
	LNode* r = L;
	cout << "请依次输入" << n << "个元素:";
	for (int i = 0;i < n;i++) {
		ElemType x;
		cin >> x;
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = NULL;
		r -> next = s;
		r = s;
	}
}

这段代码干的事就是:

“从键盘读 n 个整数,用尾插法把它们依次接成一条带头结点的单链表。”

逐句直译:

1. `InitList(L);`

   先给链表弄个头结点,里面不放数据,`L->next` 现在是 `nullptr`。

2. `LNode* r = L;`

   `r` 叫“尾指针”,永远指向当前链表的最后一个结点。一开始链表只有头结点,所以 `r` 就是 `L`。

3. `for (int i = 0; i < n; ++i)`

   循环 `n` 次,每次读一个数。

4. `cin >> x;`

   从键盘读一个整数到 `x`。

5. `LNode* s = (LNode*)malloc(sizeof(LNode));`

   在堆里新生成一个空结点,让 `s` 指向它。

6. `s->data = x;`

   把刚读到的整数塞进新结点的数据域。

7. `s->next = nullptr;`

   新结点后面暂时没东西,所以先指向空。

8. `r->next = s;`

   把新结点接在尾指针 `r` 的后面。

9. `r = s;`

   尾指针往前挪一步,现在 `r` 又指向“最新”的最后一个结点了。

循环结束后,链表顺序就是键盘输入的顺序,头结点还在最前面,不保存有效数据。

解释3:
//打印链表
void PrintList(LinkList L) {
	LNode* p = L->next;
	cout << "当前链表为:";
	while (p) {
		cout << p->data << (p->next ? "->" : "");
		p = p->next;
	}
	cout << endl;
}

一句话:

“跳过头结点,把链表里的有效数据按顺序打印成  `a -> b -> c`  这种形式。”

逐句解释:

1. `LNode* p = L->next;`

   头结点不存数据,从第一个真正结点开始。

2. `while (p)`

   只要当前结点存在就继续。

3. `cout << p->data`

   输出当前结点的数据。

4. `(p->next ? " -> " : "")`

   如果后面还有结点,就输出箭头,否则什么都不输出(避免末尾多一个 `->`)。

5. `p = p->next;`

   指针往后挪一个。

 

2.单链表的按位序插入(不带头结点)

 

如果不带头结点,则插入,删除第1个元素时,需要更改头指针L;

#include <iostream>
#include <cstdlib>
using namespace std;

using ElemType = int;

typedef struct LNode {
	ElemType data;
	struct LNode* next;
}LNode,*LinkList;
 

bool ListInsert(LinkList& L, int i, ElemType e) {
	if (i < 1) {
		return false;     //非法位序
	}
	LNode* p = L;     //p指向头结点
	int j = 0;        //j表示p的位序(头结点算0)
	 
	while (p != NULL && j < i - 1) {    // 找到第 i-1 个结点
		p = p->next;  
		j++;
	}
	if (p == NULL) {     // i 过大,链表不够长 
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}


/* 辅助:初始化一个空链表(只含头结点) */
void InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));
	L->next = nullptr;
}

//尾插法建表,方便测试
void CreateListTail(LinkList& L, int n) {
	InitList(L);
	LNode* r = L;
	cout << "请依次输入" << n << "个元素:";
	for (int i = 0;i < n;i++) {
		ElemType x;
		cin >> x;
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = NULL;
		r -> next = s;
		r = s;
	}
}


//打印链表
void PrintList(LinkList L) {
	LNode* p = L->next;
	cout << "当前链表为:";
	while (p) {
		cout << p->data << (p->next ? "->" : "");
		p = p->next;
	}
	cout << endl;
}


/* 测试 */
int main() {
	LinkList L;
	InitList(L);          // 空链表

	/* 手动插入几个元素 */
	ListInsert(L, 1, 10); // 第 1 位插入 10
	ListInsert(L, 2, 20); // 第 2 位插入 20
	ListInsert(L, 1, 5);  // 再插到最前面
	PrintList(L);         // 应输出:5 -> 10 -> 20

	/* 交互式测试 */
	int pos, val;
	cout << "请输入要插入的位序 和 元素值(位序<1结束):";
	while (cin >> pos >> val) {
		if (pos < 1) break;
		if (ListInsert(L, pos, val))
			PrintList(L);
		else
			cout << "插入失败,位序非法或内存不足!" << endl;
	}

	 
	return 0;
}

运行结果:

3.单链表的删除

   1.按位序删除(删除第  i  个结点,数据带回)
bool ListDelete(LinkList &L, int i, ElemType &e){
    if (i < 1) return false;          // 位序非法

    LNode *p = L;                     // p 指向头结点(位序 0)
    int j = 0;
    while (p && j < i - 1){           // 找第 i-1 个结点
        p = p->next;
        ++j;
    }
    if (!p || !p->next) return false; // i 过大或已到表尾

    LNode *q = p->next;               // q 指向待删结点(第 i 个)
    e = q->data;                      // 带回数据
    p->next = q->next;                // 跨过待删结点
    free(q);
    return true;
}
2. 指定结点删除(偷后继法,O(1),p 不能是尾结点)
bool DeleteNode(LNode *p){
    if (!p || !p->next) return false; // p 为空或是尾结点则失败
    LNode *q = p->next;               // q 是后继
    p->data = q->data;                // 数据搬上来
    p->next = q->next;                // 跨过后继
    free(q);
    return true;
}

使用示例:

LinkList L;
InitList(L);               // 建带头结点的空表
/* …建表… */

int x;
if (ListDelete(L, 3, x)) cout << "被删元素=" << x << endl;

LNode *p = GetElem(L, 2);  // 拿到已知结点
if (DeleteNode(p)) cout << "指定结点已删除" << endl;

链表数据结构图解 项目中经常会用到LinkedList集合来存储数据,打算写一篇LinkedList的源码解析,而LinkedList是基于链表结构存储数据的,这篇博文将解析链表数据结构,包括单向链表和双向链表; 1:单向链表: 单向链表的链表对象维护了一个 first 引用,该引用指向节点链表中的第一个节点对象,每个节点对象维护一个 next 引用,next引用指向下一个节点对象;(这里注意:是引用指向的是节点对... 阅读详情

相关推荐

扣子工作流实战演练

例如,你可以在代码节点的输入中引用大模型节点的输出,这样代码节点就可以使用大模型节点的输出。扣子的工作流节点并不是固定的,而是由一系列可以任意添加的节点组成的,每个节点都有其特定的功能和用途。扣子工作流中常见的节点类型如图4-2所示。在图像生成节点中设置模型为人像,输入参数名为input,变量值引用开始节点的renwu,正向提示词为{{input}},如图4-19所示。然后添加两个节点,选择“图像生成”和“画板”,将“开始”节点、“图像生成”节点、“画板”节点、“结束”节点依次串联,如图4-17所示。

夏天又到了的专栏 1587

数据结构——链表

轻松无痛玩转链表

HZzzzzLu的博客 1万+

踩坑记录:服务商模式下微信小程序支付,为什么必须用子商户的AppID获取openid?

本文深入解析了服务商模式下微信小程序支付必须使用子商户AppID获取openid的技术原因,详细对比了服务商模式与直连模式的关键差异,并提供了正确的配置指南和常见问题解决方案,帮助开发者避免微信支付集成中的常见陷阱。

weixin_28318011的博客 550

数据结构之链表

链表是由节点组成的线性数据结构,每个节点包含数据域和指针域,通过指针连接。主要类型包括单链表、双链表和循环链表。链表节点分散存储,内存不要求连续。操作包括初始化、插入/删除节点(O(1)复杂度)、访问/查找节点(O(n)复杂度)。相比数组,链表插入删除高效但访问较慢,更适合频繁增删场景。数组和链表在存储策略和操作效率上呈现对立特点。

2401_83194362的博客 2445

FSL软件安装

FSL软件安装

huang_tianyuan的博客 2631

SAP采购含税价转换的底层逻辑解析:ABAP例程如何实现价税分离计算

本文深入解析SAP系统中采购含税价与不含税价的转换机制,重点介绍VOFM例程和ABAP代码实现价税分离计算的方法。通过详细讲解数学原理、税率体系及SAP定价引擎架构,为开发人员提供完整的解决方案,帮助提升采购业务中的价格处理效率和准确性。

weixin_29237635的博客 450

链表数据结构

链表数据结构1 历史2 结构2.1 单向链表2.2 双向链表2.3 循环链表3链表的应用 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间...

Yuqbd的博客 990

ROS2 Jazzy 学习路线图(2026最新)

摘要: ROS2 Jazzy(LTS版)是2026年长期支持版本,仅适配Ubuntu 24.04。本文提供WSL环境下的学习路线图: 环境搭建(1-2天):配置WSL2+Ubuntu 24.04,安装ROS2 Jazzy及图形工具(VcXsrv、VS Code)。 核心概念(3-5天):掌握节点、话题、服务等通信机制,使用turtlesim和小海龟仿真实践。 开发实战(7-10天):Python/C++节点开发,结合RViz2、Gazebo等工具完成多节点通信和避障项目。 进阶应用(10-15天):导航(N

zheng_ruiguo的专栏 640

【数据结构】链表

本文详细介绍了单向链表和双向链表的实现原理及基本操作。在单向链表部分,讲解了链表结构定义、打印、尾插/头插、尾删/头删等操作的实现方法,重点分析了指针传递问题(需使用二级指针修改头节点)。双向链表部分则展示了带头双向循环链表的结构优势,详细说明了初始化、插入、删除等操作的实现逻辑,强调通过画图辅助理解指针操作。文章还对比了两种链表的特点:单向链表结构简单但功能有限,适合作为子结构;双向链表结构复杂但操作高效,适合单独存储数据。所有代码示例都配有详细注释,帮助理解指针操作和边界条件处理。

2402_83411382的博客 1783

2019美亚杯团体赛刷题 部分题解(1-42、106-116)

文章目录写在前面使用工具刷题1.在黑客路由器里,有一台设备的MAC地址为00:11:6B:47:4D:55,请问它的IP地址是什么?2.在黑客路由器里,发现一设备的IP地址为192.168.0.103,请问该设备为如下哪一个:3.警方已经查证所有连接此router的设备都归黑客所有,根據黑客路由器的訊息,下列哪组(设备---ip)是错误的:4.Hacker现场检获的Windows主机硬盘已成功取证并制作成镜像Win1.E01,下列哪个是其硬盘证据文件的MD5哈希值。5.MD5hash算法会产生一个什么大小的

7ruth0hn的博客 7548

YOLOv8 TensorRT C++部署实战详解:从XMake构建到推理流水线

本项目实现了一个基于YOLOv8和TensorRT的高性能火灾烟雾检测系统,具有以下核心特点: 技术架构:采用TensorRT 8.6+进行GPU加速推理,结合CUDA 12.1和OpenCV 4.8+,实现高效计算机视觉处理。系统使用XMake替代CMake构建,配置更简洁高效。

hscing的博客 1065

C语言数据结构-链表

图中指针变量plist保存的是第一个结点的地址,我们称plist此时“指向”第一个结点,如果我们希望plist“指向”第二个结点时,只需要修改plist保存的内容为0x0012FFA0.在前面说的单链表中的“头结点”,该头结点是链表的首节点(第一个结点),实际这样的称呼是错误的,因为链表中存在一种链表叫做带头链表(不是指链表里第一个有效的结点),这里的头结点指的是,在这里直接使用plist来遍历结果也是一样的,重新创建个pcur指针,是为了避免由于指针指向的改变,导致无法重新找到链表的首结点。

2302_81072215的博客 2261

OpenClaw进行安全测试(附使用场景案例)

test-master 是一个全面的测试专家,覆盖功能测试、性能测试、安全测试三大领域。这里我们重点来看下使用test-master如何进行安全测试。使用 test-master 时,可以针对以下高频领域进行专项安全扫描:✅ OWASP Top 10 测试✅ SQL 注入测试✅ XSS 测试✅ CSRF 测试✅ 认证 / 授权测试。

十二测试录 331

数据结构-链表篇

数据结构-链表篇

qq_16268979的博客 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

链表-Java实现链表数据结构

链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上 一个/或下一个节点的位置的链接(“links”) 链表(Linked list):是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数 据,而是在每一个节点里存到下一个节点的指针(Pointer)。 使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针 域,空间开销比较.

♾️ 2738

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

链表【C++数据结构】

本文系统讲解链表指针原理与实现,对比std::list和vector,并深入分析了其在频繁插入/删除场景(如LRU缓存)下的独特性能优势与应用价值。

不会C++、害怕嵌入式且不爱看动漫的小趴菜 1万+

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

数据结构【链表】

是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量)。给定的链表的结构,实现链表从头到尾的打印。

2401_84320036的博客 1653

【数据结构】链表篇

手把手教你实现链表~

Yui_的博客 1592

【数据结构】链表详解

【数据结构】链表详解

m0_71214261的博客 1299

[数据结构]——链表详解

数据结构---链表详解

m0_62023005的博客 4459

JavaScript数据结构——链表

前言 嘿,掘友们!今天我们来了解并实现数据结构 —— 链表。 本文主要内容 单向链表 双向链表 循环链表 链表 要存储多个元素,数组可能是最常用的数据结构。但是这种数据结构有一个缺点,数组的大小是固定的,从数组的起点或中间插入或移除的成本很高,因为需要移动元素。 链表存储有序的元素集合,不同于数组的是,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(指针或链接)组成。下图是一个链表的结构。 相比数组,链表的一个好处在于,添加或移除元素的时候不需要移动其

郊眠寺 1483
上一篇: 暴力匹配vs KMP实测:nm到底差多少?附可运行源码+时间复杂度对比
遗憾是什么.
博客等级 码龄1年 904粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遗憾是什么.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值