目录

结构推导

回到最原始的问题 —— 我们如何存数据?

第二步:我们来看看数组的限制 

第三步:那我们该怎么做呢? 

第四步:我们推导链表的数据结构 

结构讲解

什么是链表?

什么是节点? 

如何创建节点?

 如何访问节点?


结构推导

🎯 目标:理解什么是链表(linked list),为什么我们需要它,以及它的结构从哪里来的。

回到最原始的问题 —— 我们如何存数据?

在 C 语言中,最基础的存数据方式是:

int a[100];

也就是 数组(array)。它非常常见,我们从一开始就用。

那我们先从数组的“第一性”特征说起:

数组的本质是:

一块连续的内存空间 + 索引访问

举个例子:

int a[5] = {10, 20, 30, 40, 50};

这个数组在内存里是这样的(假设从地址 1000 开始):

地址
100010
100420
100830
101240
101650

它的特性:

  • 地址连续

  • a[i] 的位置是:a + i × sizeof(int)

  • 可以快速通过索引访问:O(1)

详细内容可以参考:数据结构:数组(Array)_array[1..50]-CSDN博客


第二步:我们来看看数组的限制 

情况1:大小固定

如果你定义了:

int a[100];

你就固定只能存最多 100 个元素。如果只用了前 5 个元素,空间也浪费。

如果你事先不知道需要多少个元素,就很难选大小。

情况2:插入麻烦

假设你已经有数组:

int a[5] = {10, 20, 30, 40, 50};

现在你想在 第 2 个位置插入 99,就得把后面的都搬一格:

for (int i = 5; i > 1; i--) {a[i] = a[i - 1];
}
a[1] = 99;

这会导致O(n) 的时间复杂度。

情况3:删除也麻烦

类似的,如果你要删掉 a[2],也得把后面的全往前搬一格。

总结数组的问题:

问题描述
空间不灵活空间必须一开始就定下来
插入慢中间插入要整体移动元素
删除慢删除某项也要移动后面的元素
空间浪费有时只用一部分数组,但仍然占内存

第三步:那我们该怎么做呢? 

我们思考一下:

能不能只在用到数据时,才开辟空间?

能不能插入一个元素,而不影响其它元素的位置?

这就引出了一个新的思维模型:

✅ 使用“分散空间 + 指针连接”的模型

如果我们不强求内存连续,而是:

  • 每次添加一个节点时,就用 malloc 单独开一块空间

  • 每个节点都保存指向“下一个节点”的指针

我们就可以实现灵活的动态结构了。

这就是 链表(linked list) 的思想:

把所有的数据节点一个一个连起来,而不是排成连续数组


第四步:我们推导链表的数据结构 

我们要存一个元素,它需要什么?

  1. 数据本身,比如:一个整数

  2. 指向下一个节点的“线索”(指针)

所以我们设计一个节点结构:

struct Node {int data;           // 当前节点的数据struct Node* next;  // 指向下一个节点
};

那一个“链表”是什么?

一个链表是“若干个 Node 连起来”,只要有第一个节点(头结点)的地址,就能找到整个链表。

所以我们定义:

struct Node* head;  // 指向链表的起点

✅ 所以链表的基本单位是:节点 + 指针

[10 | *] --> [20 | *] --> [30 | NULL]
  • 每个方块是一个 Node

  • *next 指针

  • 最后一个节点的 next = NULL 表示链表结束

结论:为什么我们需要链表?

问题

链表怎么解决

数组大小固定

链表可以动态增加节点(malloc)

插入/删除慢

链表插入删除只需修改指针,不搬数据

空间浪费

用多少开多少,不多开


结构讲解

什么是链表?

在数组中,所有元素都在一块连续的内存空间中。你只能在一开始就分配好大小,插入/删除不方便。那么:

有没有一种数据结构,不要求数据连续存储,但依然能一个接一个地串起来?

 这就是链表的本质:

链表是由若干个节点组成的线性结构,每个节点都知道下一个节点在哪里,只需要记住第一个节点就能访问整个链表。

链表的核心思想是:

每个节点知道“谁在后面”,就像每一节车厢都知道后面那节车厢的位置。

整个链表就像是一列火车,每节车厢通过“挂钩”(指针)串联起来:

✅ 链表的特征:

  • 节点之间不要求连续内存

  • 每个节点都包含:

    • 本节点的数据

    • 指向下一个节点的指针

  • 通过“指针”把节点连接起来

 通常我们用一个指针 head 来指向第一个节点:

struct Node* head;

什么是节点? 

一个节点(Node)是链表的最基本组成单位,就是一个结构体,它包含:

  1. 一份数据(例如 int 类型)

  2. 一个指针,指向下一个节点

这就好比:

  • 火车的每节车厢(节点)都有:

    • 货物(数据)

    • 连接器(指针)去连下一节车厢

 用C语言表示:

struct Node {int data;           // 节点中存的数据struct Node* next;  // 指向下一个节点的指针
};
成员含义
int data存储数据,比如整数 10
Node* next保存下一个节点的地址,如果是最后一个就为 NULL

如何创建节点?

为什么我们不能直接写:

struct Node node1;

1. 这句代码在 栈(stack)上 分配了一个 Node 类型的变量。它的特点是:

  • 分配在栈上(函数调用结束后会被自动释放)

  • 内存大小在编译时就已经确定

  • 生命周期由作用域决定,作用域结束就失效

🔍 2. 链表是动态结构,不能预知节点个数

链表的本质是:

  • 你不知道总共有多少个节点(比如根据用户输入构建)

  • 每一个节点都需要“现用现开”,而不是提前一次性分配

所以我们需要在“运行时”动态分配空间: 

struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));

这行代码的含义是:

  1. malloc(sizeof(struct Node))堆(heap)上动态分配一块内存,大小正好是一个 Node

  2. 返回值是 void*,强制类型转换成 struct Node*

  3. newNode 是一个指针,指向新建的节点

再从链表的设计需求来倒推

  • 节点数是动态变化的(例如用户输入10个节点)

  • 节点之间是用指针连接的

  • 每个节点可能在任何时刻被插入或删除

  • 节点需要长期存在,甚至跨越函数作用域

 所以:必须用动态内存分配,也就是 malloc()。struct Node node1; 是不能满足上述需求的


 如何访问节点?

假设我们已经创建了多个节点,并链接起来:

struct Node* head = (struct Node*) malloc(sizeof(struct Node));
head->data = 10;struct Node* second = (struct Node*) malloc(sizeof(struct Node));
second->data = 20;head->next = second;
second->next = NULL;

访问链表节点的本质逻辑是沿着 next 指针走,这就叫做遍历链表(traverse the linked list)。

举个例子,假设你有一个链表,要访问节点内容:

head → [10 | * ] → [20 | * ] → [30 | NULL]

你想访问第二个节点的值 20:

你不能说 head[1],因为它不是数组。

你只能说:

head->next->data

这个表达式的意义是:

  1. head 是第一个节点的地址

  2. head->next 是第二个节点的地址

  3. head->next->data 就是第二个节点的值

访问第二个节点的数据: 

printf("%d\n", head->next->data);  // 输出 20

遍历就是:

head 开始,沿着每个节点的 next 指针走,直到你走到 NULL(链表结尾)

struct Node* temp = head;
while (temp != NULL) {printf("%d ", temp->data);temp = temp->next;
}
printf("\n");

这段代码会访问每个节点的 data,直到遇到 NULL。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91511.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91511.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/91511.shtml

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

相关文章

[RK3566-Android11] U盘频繁快速插拔识别问题

问题描述 做老化测试时,在使用U盘频繁快速插拔的情况下,SDCard目录会突然被Kill掉,然后又重新挂载上,这会导致系统及APP的数据因为读写异常,从而界面卡死正常U盘插拔不应该导致内部存储卸载解决方案: SDK根…

【Golang】Go语言Map数据类型

Go语言Map数据类型 文章目录Go语言Map数据类型一、Map1.1.1、map定义1.1.2、map的基本使用1.1.3、判断某个键是否存在1.1.4、map的遍历1.1.5、使用delete()函数删除键值对1.1.6、按照指定顺序遍历map1.1.7、元素为map类型的切片1.1.8、值为切片类型的map一、Map map是一种无序…

Orange的运维学习日记--23.Linux计划任务详解

Orange的运维学习日记–23.Linux计划任务详解 文章目录Orange的运维学习日记--23.Linux计划任务详解一次性计划任务atd 服务at 命令基本语法交互式示例脚本文件示例timespec 格式示例查看与管理任务查看当前队列查看任务详细内容删除任务用户权限控制用户周期性计划任务查看任务…

Ubuntu 24.04.2 LTS 安装mysql8.0.36保姆级教程(从安装到远程连接)

目录 前言 一、系统准备 二、安装 MySQL 8.0.36 1. 查看可用版本 2.如果没有对应版本则需要手动下载mysql-apt-config(有则跳过) 2.1下图是mysql-apt-config各版本对应的mysql版本 2.2下载mysql apt repository 2.3安装 MySQL APT Repository 包 …

【LLM】讲清楚MLA原理

需要你对MHA、MQA、GQA有足够了解,相信本文能帮助你对MLA有新的认识。 本文内容都来自https://www.youtube.com/watch?v0VLAoVGf_74,如果阅读本文出现问题,建议直接去看一遍。 按照Deepseek设定一些参数值:输入token长度n10&…

谷歌采用 Ligero 构建其 ZK 技术栈

1. 引言 前序博客有: Ligero 和 Ligetron 中的 MPC 和 ZKLigetron:Nim Network开发的针对AI的zkVMLigetron:基于MPC-In-The-Head范式的zkVM简介 在隐私保护身份验证领域迈出重要一步,谷歌最近宣布 将零知识证明(ZKP…

Flutter渲染引擎:Impeller和Skia

一、Impeller 渲染引擎的发布时间Impeller 是 Flutter 团队为解决 Skia 引擎在移动端(尤其是 iOS 平台)的性能问题而开发的全新渲染引擎,其发展历程如下:首次公开:2021 年 Google I/O 大会上首次提及,作为 …

网络编程-加密算法

目录 一.网络编程基础 1. 概述 2. IP地址 3. 域名 4. 网络模型 5. 常用协议 6. 小结 二.TCP编程 1. 什么是Socket? 2. 服务器端 3. 客户端 4. Socket流 5. 小结 三.UDP编程 1. 概述 2. 服务器端 3. 客户端 4. 小结 案例: 四.加密算法 …

【网络工程师软考版】网络安全

任何形式的网络服务都会导致安全方面的风险,问题是如何将风险降到最低程度,目前的网络安全措施有数据加密、数字签名、身份认证、防火墙、特征过滤等。所涉内容:1、网络安全基础2、加密技术与哈希算法3、数字签名4、数字证书5、VPN技术6、防火…

深入浅出设计模式——创建型模式之建造者模式 Builder

文章目录建造者模式简介建造者模式结构建造者模式代码实例定义产品类House定义建造者定义抽象建造者AbstractBuilder定义具体建造者定义指挥者客户端代码示例运行结果建造者模式总结代码仓库建一栋房子总共分几步?建造者模式告诉你答案!“把大象装冰箱&a…

OpenVLA: 论文阅读 -- 开源视觉-语言-行动模型

更多内容:XiaoJ的知识星球 目录OpenVLA:开源视觉-语言-行动模型1. 介绍2. 相关工作1)视觉条件语言模型(Visually-Conditioned Language Models)2)通用型机器人策略(Generalist Robot Policies&a…

JavaWeb(苍穹外卖)--学习笔记15(分页查询PageHelper)

前言 终于开始学习做项目了,本篇文章是学习B站黑马程序员苍穹外卖的学习笔记📑。我的学习路线是Java基础语法-JavaWeb-做项目,管理端的功能学习完之后,就进入到了用户端微信小程序的开发,这篇文章来看看分页查询&#…

金融专题|某跨境支付机构:以榫卯企业云平台 VPC 功能保障业务主体安全

作者:SmartX 金融团队 金融机构在信息化建设时面临诸多数据合规要求,例如:不同业务区域之间互相隔离、数据库仅能由关联的应用服务器访问、仅有特定的服务器允许被外网访问等。对此,某跨境支付机构以 SmartX 榫卯企业云平台构建私…

Win10下python环境变量呼出微软应用商店

以下是三种彻底解决 Windows 10 的 CMD 中运行 python 命令弹出应用商店问题的方法​​方法一:调整环境变量优先级​-或者直接删除微软应用商店的环境变量%USERPROFILE%\AppData\Local\Microsoft\WindowsApp​​​操作步骤​​打开系统环境变量设置(右键…

字节跳动“扣子”(Coze)开源:AI智能体生态的技术革命

(以下借助 DeepSeek-R1 辅助整理) 在2025年7月26日的深夜,GitHub上悄然出现的两个仓库——Coze Studio和Coze Loop,在48小时内狂揽超过9,000颗Star。字节跳动以Apache 2.0许可证将自家AI智能体平台的核心技术彻底开源。 “当所有人…

Camx-usecase ID和pipeline的匹配源码解读

组件关系整体流程:camxhal3.cpp:704 open()camxhal3.cpp:1423 configure_streams()chxextensionmodule.cpp:2810 InitializeOverrideSessionchxusecaseutils.cpp:850 GetMatchingUsecase()chxadvancedcamerausecase.cpp:4729 Initialize()chxadvancedcamerausecase.…

日志管理进入「对话式」时代:日志易MCP Server落地实录

01 背景:MCP协议介绍在AI蓬勃发展的当下,大型语言模型(LLM)虽展现出强大潜力,却受困于与外部资源连接的难题。数据分散、接口繁杂,致使AI模型难以灵活对接本地资源与远程服务,极大限制了其响应质…

django-3模型操作

from django.db import modelsclass Book(models.Model):title models.CharField(max_length200) # 书名author models.CharField(max_length100) # 作者publish_date models.DateField() # 出版日期price models.DecimalField(max_digits10, decimal_places2) # 价格s…

【绘制图像轮廓】——图像预处理(OpenCV)

目录 1 什么是轮廓 2 寻找轮廓 2.1 mode参数 2.2 method参数 3 绘制轮廓 1 什么是轮廓 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形。轮廓是连续的,边缘不一定连续。轮廓是一个闭合的、封闭的形状。 轮廓的作用: 形状分析 目…

嵌入式 Linux 深度解析:架构、原理与工程实践(增强版)

嵌入式 Linux 深度解析:架构、原理与工程实践(增强版) 目录嵌入式 Linux 深度解析:架构、原理与工程实践(增强版)第一章 嵌入式 Linux 基础概念1.1 定义与核心特征1.2 典型架构栈深度解析第二章 Linux 文件…