1. 双向链表与循环链表的核心概念双向链表是一种比单向链表更高级的数据结构它在每个节点中除了存储数据和指向下一个节点的指针(next)外还增加了一个指向前一个节点的指针(prev)。这种结构使得链表可以双向遍历为许多操作带来了便利。1.1 双向链表的基本结构双向链表的节点通常包含三个部分数据域(data)存储节点的实际数据前驱指针(prev)指向当前节点的前一个节点后继指针(next)指向当前节点的后一个节点这种结构使得我们可以从任意节点出发向前或向后遍历整个链表。在实际应用中双向链表通常会维护头节点(head)和尾节点(tail)的引用以便快速访问链表的首尾。注意双向链表虽然功能更强大但每个节点需要额外的空间存储前驱指针因此在内存使用上会比单向链表多占用约33%的空间假设指针和数据占用相同大小的内存。1.2 循环链表的特性循环链表是双向链表的一种变体它将链表的首尾相连形成一个环。具体表现为头节点的prev指针指向尾节点尾节点的next指针指向头节点这种结构消除了链表的端点概念使得遍历可以从任意节点开始并无限循环下去。循环链表特别适合需要周期性处理数据的场景如操作系统中的进程调度。2. 双向链表的操作与实现2.1 基本操作的时间复杂度双向链表支持的高效操作包括操作类型时间复杂度说明头部插入O(1)直接修改head指针尾部插入O(1)直接修改tail指针指定节点前插入O(1)利用prev指针直接访问前驱指定节点后插入O(1)利用next指针直接访问后继删除指定节点O(1)直接调整相邻节点的指针按值查找O(n)需要遍历整个链表2.2 Java实现示例public class DoublyLinkedListT { private NodeT head; private NodeT tail; private int size; private static class NodeT { T data; NodeT prev; NodeT next; Node(T data, NodeT prev, NodeT next) { this.data data; this.prev prev; this.next next; } } // 在链表头部添加元素 public void addFirst(T item) { NodeT newNode new Node(item, null, head); if (head ! null) { head.prev newNode; } else { tail newNode; // 链表为空时新节点也是尾节点 } head newNode; size; } // 在链表尾部添加元素 public void addLast(T item) { NodeT newNode new Node(item, tail, null); if (tail ! null) { tail.next newNode; } else { head newNode; // 链表为空时新节点也是头节点 } tail newNode; size; } // 删除指定节点 public void remove(NodeT node) { if (node null) return; if (node.prev ! null) { node.prev.next node.next; } else { head node.next; // 删除的是头节点 } if (node.next ! null) { node.next.prev node.prev; } else { tail node.prev; // 删除的是尾节点 } size--; } }2.3 操作中的注意事项边界条件处理始终要考虑链表为空、只有一个节点、操作头节点或尾节点等特殊情况。指针更新顺序在插入或删除节点时指针更新的顺序很重要。错误的顺序可能导致链表断裂。一般原则是先处理新节点的指针再处理周围节点的指针。内存泄漏在删除节点时特别是在没有自动垃圾回收的语言中要确保正确释放节点内存。3. 循环链表的实现与应用3.1 循环链表的实现要点将普通双向链表改造成循环链表只需在初始化或修改链表时维护首尾连接public class CircularDoublyLinkedListT extends DoublyLinkedListT { Override public void addFirst(T item) { super.addFirst(item); // 维护循环关系 head.prev tail; tail.next head; } Override public void addLast(T item) { super.addLast(item); // 维护循环关系 head.prev tail; tail.next head; } }3.2 循环链表的典型应用场景音乐播放列表实现循环播放功能轮询任务调度公平分配CPU时间片多人游戏轮次循环切换玩家回合缓存淘汰算法如Clock算法4. 链表操作中的常见问题与解决方案4.1 指针丢失问题在修改链表结构时最常见的错误是丢失对节点的引用。例如// 错误的插入方式 - 会导致链表断裂 node.next newNode; newNode.prev node; newNode.next node.next; // 此时node.next已经是newNode本身正确的做法是使用临时变量保存原指针NodeT temp node.next; node.next newNode; newNode.prev node; newNode.next temp; if (temp ! null) { temp.prev newNode; }4.2 循环引用检测在循环链表中不当的操作可能导致意外的循环引用。可以使用快慢指针算法检测public boolean hasCycle() { if (head null) return false; NodeT slow head; NodeT fast head.next; while (fast ! null fast.next ! null) { if (slow fast) return true; slow slow.next; fast fast.next.next; } return false; }4.3 性能优化技巧尾指针缓存对于频繁的尾部操作维护tail指针可以显著提高性能。大小记录维护size变量可以快速获取链表长度避免遍历计数。哨兵节点使用空节点作为头尾哨兵可以简化边界条件处理。5. 链表与数组的对比选择在实际开发中选择数据结构时需要综合考虑多种因素考虑因素数组链表内存连续性连续非连续随机访问O(1)O(n)插入/删除O(n)O(1)内存使用紧凑额外指针开销CPU缓存友好是否大小调整固定/昂贵动态/廉价选择建议需要快速随机访问 → 选择数组频繁插入删除 → 选择链表内存受限 → 优先考虑数组需要动态大小 → 选择链表6. 实际工程中的应用实例6.1 Java集合框架中的LinkedListJava的LinkedList是基于双向链表实现的它提供了丰富的操作方法LinkedListString list new LinkedList(); list.addFirst(A); // 头部插入 list.addLast(B); // 尾部插入 list.removeFirst(); // 头部删除 list.removeLast(); // 尾部删除6.2 实现LRU缓存双向链表非常适合实现LRU(最近最少使用)缓存淘汰策略public class LRUCacheK, V { private final int capacity; private final MapK, NodeV map; private final DoublyLinkedListV list; public LRUCache(int capacity) { this.capacity capacity; this.map new HashMap(); this.list new DoublyLinkedList(); } public V get(K key) { if (!map.containsKey(key)) return null; NodeV node map.get(key); list.remove(node); list.addFirst(node.data); return node.data; } public void put(K key, V value) { if (map.containsKey(key)) { list.remove(map.get(key)); } else if (map.size() capacity) { NodeV last list.getTail(); list.remove(last); // 也需要从map中移除对应的key } list.addFirst(value); map.put(key, list.getHead()); } }6.3 浏览器历史记录浏览器的前进后退功能可以通过双向链表实现public class BrowserHistory { private NodeString current; public BrowserHistory(String homepage) { current new Node(homepage, null, null); } public void visit(String url) { NodeString newNode new Node(url, current, null); current.next newNode; current newNode; } public String back(int steps) { while (steps-- 0 current.prev ! null) { current current.prev; } return current.data; } public String forward(int steps) { while (steps-- 0 current.next ! null) { current current.next; } return current.data; } }在实际编码中双向链表虽然需要更多的内存来存储额外的指针但它带来的操作便利性往往值得这种空间开销。特别是在需要频繁在任意位置插入删除、或者需要双向遍历的场景下双向链表相比数组和单向链表都有明显的优势。
企业数字化 ERP 产品动态
相关推荐
OpenFOAM风工程源码到求解器知识地图与避坑指南 简介:这份PDF面向刚接触开源CFD的初学者与风工程方向研究者,系统梳理OpenFOAM的来龙去脉与核心能力,帮助读者在动手建模前建立对软件架构的整体认知。全文围绕有限体积离散、非结构化多面体网格、面向对象类库封装、大规模并行计算等特性展开… · 2026/11/5 16:42:23
CUDA与Triton编程范式本质对比:线程自由vs块级契约 1. 为什么“线程级的自由,块级的契约”不是一句口号,而是两种编程模型的根本分水岭我第一次在 NVIDIA GTC 大会上听到这个说法时,下意识觉得是营销话术——不就是写 GPU kernel 吗?CUDA 写 launch 参数,Triton 写 trit… · 2026/11/5 10:06:34
DevilutionX:让《暗黑破坏神》与《地狱火》在任意现代设备上跑起来的实测指南 DevilutionX:让《暗黑破坏神》与《地狱火》在任意现代设备上跑起来的实测指南 【免费下载链接】DevilutionX Diablo build for modern operating systems 项目地址: https://gitcode.com/gh_mirrors/de/DevilutionX
想今天再玩一把《暗黑破坏神》,… · 2026/11/6 1:32:23
考研408《操作系统》复习笔记,第四章《4.1.2 文件外存存储管理》 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:32:23
CDD文件DID在CANdelaStudio和CANoe显示名称不一致 问题描述开发完一些诊断的DID,需要进行自测,发现一个奇怪的问题,明明是同一个CDD文件,它的DID名称在CANdelaStudio和CANoe显示完全不是同一个。问题分析一开始怀疑是配置有问题,因为在CANdelaStudio软件里面࿰… · 2026/11/6 1:32:23
1553B组网全攻略:从零搭建一套完整的总线系统需要哪些组件? /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:32:23
AI茄子移栽机器人 QT信创完整工程 # AI茄子移栽机器人 QT信创完整工程 ## 项目说明 适配**统信UOS、银河麒麟**国产信创Qt5.12/5.15,针对**茄子幼苗**专属视觉识别(茄子苗叶片狭长、紫茎特征区分番茄),穴盘苗壮苗筛选、机械臂移栽轨迹规划、RS485串口运动控制、移栽产量统计、本地日志存储、离线图片推理,… · 2026/11/6 1:31:23
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
ASIC主模式RTL设计:状态机与三态驱动的流片级实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07