单/双链表代码实现

简介: 本文详解单/双链表的代码实现,涵盖增删查改操作。重点解析三大技巧:1)同时持有头尾节点引用以优化插入删除效率;2)使用虚拟头尾节点简化边界处理;3)避免内存泄漏的良好编程习惯。适合掌握链表基础后深入学习。

单/双链表代码实现
❗前置知识
阅读本文前,请学习:链表(链式存储)基本原理
几个关键点
下面我会分别用双链表和单链给出一个简单的 MyLinkedList 代码实现,包含了基本的增删查改功能。这里给出几个关键点,等会你看代码的时候可以着重注意一下。
关键点一、同时持有头尾节点的引用
在力扣做题时,一般题目给我们传入的就是单链表的头指针。但是在实际开发中,用的都是双链表,而双链表一般会同时持有头尾节点的引用。
因为在软件开发中,在容器尾部添加元素是个非常高频的操作,双链表持有尾部节点的引用,就可以在 O(1)的时间复杂度内完成尾部添加元素的操作。
对于单链表来说,持有尾部节点的引用也有优化效果。比如你要在单链表尾部添加元素,如果没有尾部节点的引用,你就需要遍历整个链表找到尾部节点,时间复杂度是 O(n);如果有尾部节点的引用,就可以在 O(1)的时间复杂度内完成尾部添加元素的操作。
细心的读者可能会说,即便如此,如果删除一次单链表的尾结点,那么之前尾结点的引用就失效了,还是需要遍历一遍链表找到尾结点。
是的,但你再仔细想想,删除单链表尾结点的时候,是不是也得遍历到倒数第二个节点(尾结点的前驱),才能通过指针操作把尾结点删掉?那么这个时候,你不就可以顺便把尾结点的引用给更新了吗?
关键点二、虚拟头尾节点
在上一篇文章 链表基础 中我提到过「虚拟头尾节点」技巧,它的原理很简单,就是在创建双链表时就创建一个虚拟头节点和一个虚拟尾节点,无论双链表是否为空,这两个节点都存在。这样就不会出现空指针的问题,可以避免很多边界情况的处理。
举例来说,假设虚拟头尾节点分别是 dummyHead 和 dummyTail,那么一条空的双链表长这样:
如果你添加 1,2,3 几个元素,那么链表长这样:
你以前要把在头部插入元素、在尾部插入元素和在中间插入元素几种情况分开讨论,现在有了头尾虚拟节点,无论链表是否为空,都只需要考虑在中间插入元素的情况就可以了,这样代码会简洁很多。
当然,虚拟头结点会多占用一点内存空间,但是比起给你解决的麻烦,这点空间消耗是划算的。
对于单链表,虚拟头结点有一定的简化作用,但虚拟尾节点没有太大作用。
虚拟节点是内部实现,对外不可见
虚拟节点是你内部实现数据结构的技巧,对外是不可见的。比如按照索引获取元素的 get(index) 方法,都是从真实节点开始计算索引,而不是从虚拟节点开始计算。
关键点三、内存泄露?
在前文 动态数组实现 中,我提到了删除元素时,要注意内存泄露的问题。那么在链表中,删除元素会不会也有内存泄露的问题呢?
尤其是这样的写法,你觉得有没有问题:
细心的读者可能认为这样写会有内存泄露的问题,因为原来的那个头结点 1 的 next 指针没有断开,依然指向着节点 2。
但实际上这样写是 OK 的,因为 Java 的垃圾回收的判断机制是看这个对象是否被别人引用,而并不会 care 这个对象是否还引用着别人。
那个节点 1 的 next 指针确实还指向着节点 2,但是并没有别的指针引用节点 1 了,所以节点 1 最终会被垃圾回收器回收释放。所以说这个场景和数组中删除元素的场景是不一样的,你可以再仔细思考一下。
不过呢,删除节点时,最好还是把被删除节点的指针都置为 null,这是个好习惯,不会有什么代价,还可能避免一些潜在的问题。所以在下面的实现中,无论是否有必要,我都会把被删除节点上的指针置为 null。
双链表代码实现
单链表代码实现
Java
运行代码
复制代码
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
import java.util.NoSuchElementException;

// ***** 改 *****

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> p = getNode(index);

    E oldVal = p.val;
    p.val = element;

    return oldVal;
}

// ***** 其他工具函数 *****
public int size() {
    return size;
}

public boolean isEmpty() {
    return size == 0;
}

private boolean isElementIndex(int index) {
    return index >= 0 && index < size;
}

private boolean isPositionIndex(int index) {
    return index >= 0 && index <= size;
}

// 检查 index 索引位置是否可以存在元素
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}

// 检查 index 索引位置是否可以添加元素
private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}

// 返回 index 对应的 Node
// 注意:请保证传入的 index 是合法的
private Node<E> getNode(int index) {
    Node<E> p = head.next;
    for (int i = 0; i < index; i++) {
        p = p.next;
    }
    return p;
}

public static void main(String[] args) {
    MyLinkedList2<Integer> list = new MyLinkedList2<>();
    list.addFirst(1);
    list.addFirst(2);
    list.addLast(3);
    list.addLast(4);
    list.add(2, 5);

    System.out.println(list.removeFirst()); // 2
    System.out.println(list.removeLast()); // 4
    System.out.println(list.remove(1)); // 5

    System.out.println(list.getFirst()); // 1
    System.out.println(list.getLast()); // 3
    System.out.println(list.get(1)); // 3
}

}

相关文章
|
23天前
|
监控 应用服务中间件 API
Agentic 应用时代,Dify 全链路可观测最佳实践
本文讲述 Dify 平台在 Agentic 应用开发中面临的可观测性挑战,从开发者与运维方双重视角出发,系统分析了当前 Dify 可观测能力的现状、局限与改进方向
334 18
Agentic 应用时代,Dify 全链路可观测最佳实践
|
1月前
|
存储 人工智能 自然语言处理
阿里云 Elasticsearch 的 AI 革新:高性能、低成本、智能化的搜索新纪元
本文介绍了数智化浪潮下, 阿里云 Elasticsearch 打通了 云原生内核优化、RAG 闭环方案、云原生推理平台 三大能力模块,实现了从底层到应用的全链路升级,助力企业构建面向未来的智能搜索中枢。
367 22
|
26天前
|
SQL JSON 分布式计算
【跨国数仓迁移最佳实践6】MaxCompute SQL语法及函数功能增强,10万条SQL转写顺利迁移
本系列文章将围绕东南亚头部科技集团的真实迁移历程展开,逐步拆解 BigQuery 迁移至 MaxCompute 过程中的关键挑战与技术创新。本篇为第六篇,MaxCompute SQL语法及函数功能增强。 注:客户背景为东南亚头部科技集团,文中用 GoTerra 表示。
235 20
|
10天前
|
人工智能 安全 开发者
解构AI时代的“深圳答案”:以硬实力构建“护城河”
2025年,深圳以“昇腾+光明实验室+华为”协同模式,打造国产AI算力生态。不同于追逐应用热点,深圳聚焦底层突破,构建从芯片到应用的全栈自主链条,通过政企联动、产学研协同,形成“技术攻关—场景验证—迭代优化”闭环,推动算力高效利用与产业深度融合,为全球AI发展提供安全可控的“中国方案”。
79 15
|
26天前
|
机器学习/深度学习 人工智能 算法
PAIFuser:面向图像视频的训练推理加速框架
阿里云PAI推出PAIFuser框架,专为视频生成模型设计,通过模型并行、量化优化、稀疏运算等技术,显著提升DiT架构的训练与推理效率。实测显示,推理耗时最高降低82.96%,训练时间减少28.13%,助力高效低成本AI视频生成。
191 22
|
19天前
|
SQL 关系型数据库 MySQL
MySQL从入门到精通:系统性学习路径
“MySQL从入门到精通”系统梳理了从基础到高阶的完整学习路径,涵盖安装配置、SQL语法、数据库设计、事务锁机制、性能优化、主从复制及分库分表等核心内容,结合实战任务帮助开发者由浅入深掌握MySQL,助力成为数据库高手。
145 13
|
19天前
|
前端开发 数据挖掘
精准类目+关键词布局,让1688商品快速获得曝光!
本文详解1688商品曝光提升策略,涵盖精准类目选择、关键词优化、流量获取及展现位竞争。通过科学布局关键词、完善商品信息、提升服务质量,助力商家精准触达客户,实现曝光与转化双增长。
|
8天前
|
JavaScript 定位技术 API
Trilium Notes:构建个人知识库的开源神器
Trilium Notes 是一款开源、免费的个人知识管理系统,支持树状结构、笔记克隆、富文本与 Markdown、代码高亮、加密笔记、思维导图及 REST API。可本地部署,跨平台同步,助力构建“第二大脑”,适合学习、研发与创意写作。
145 8
 Trilium Notes:构建个人知识库的开源神器
|
14天前
|
人工智能 安全 调度
一文详解容器服务面向大模型和 AI Agent 的技术变革
在生成式人工智能迅猛发展的浪潮下,企业应用正加速从模型研究走向业务落地。无论是大规模的数据处理、超大参数模型的训练与推理,还是部署能够自动完成任务的 AI Agent,这些场景都需要稳定、高效且可弹性伸缩的资源调度与管理能力。容器凭借环境一致性、跨平台部署和高效调度等优势,天然契合 AI 场景对多样化算力、快速迭代和规模化分发的要求,成为 AI 时代事实上的原生基石。然而,要满足在生产规模下的需求,产品及技术形态需随之演进。
169 3
|
26天前
|
SQL 分布式计算 大数据
【跨国数仓迁移最佳实践8】MaxCompute Streaming Insert:大数据数据流写业务迁移的实践与突破
本系列文章将围绕东南亚头部科技集团的真实迁移历程展开,逐步拆解 BigQuery 迁移至 MaxCompute 过程中的关键挑战与技术创新。本篇为第八篇,MaxCompute Streaming Insert:大数据数据流写业务迁移的实践与突破。 注:客户背景为东南亚头部科技集团,文中用 GoTerra 表示。
266 38