Java数据结构 (从0构建链表(LinkedList))

server/2025/1/24 11:59:07/

在本文中,我们将基于 MySingleLinkedList 类,深入探讨单链表的实现,包括创建、插入、删除等核心操作,同时分享完整的代码示例。单链表是一种灵活的数据结构,适用于处理需要频繁插入和删除操作的场景,例如实现队列 📜、链式栈 🔥、循环队列 🔄 等。在计算机科学和实际开发中,链表结构被广泛应用。它不仅可以高效地处理插入和删除操作,还能够根据实际需求实现不同的链表结构。🚀本篇不仅涵盖了基本功能的代码展示,还补充了各种操作的实现细节和优化建议,旨在让读者能够全面掌握单链表的使用与实现。😊💡

目录

链表的基本结构 📊

代码实现 🖥️

讲解 🧑‍🏫

a. 链表的基本操作 🔧

1. 链表的创建 🛠️

动态创建链表 💻

讲解 💡

2. 链表的遍历 🔍

讲解 📚

3. 获取链表长度 🔢

讲解 🧐

b. 插入操作 ➕

1. 头插法 🏁

讲解 ⚡

2. 尾插法 🔚

讲解 📈

3. 任意位置插入 🔄

讲解 📘

c. 删除操作 ❌

1. 删除第一个匹配的节点

讲解 🧠

2. 删除所有匹配的节点

讲解 🔍

3. 清空链表

讲解 💬

总结 🌟



链表的基本结构 📊

链表由节点(ListNode)组成,每个节点包含三个部分:

  • 值域(val):存储节点的值。💎
  • 指针域(next):指向下一个节点。👉
  • 前驱指针(prev):尽管在单链表中未实际使用,它可以支持双向链表的扩展。🔄

代码实现 🖥️

以下是链表的基础结构代码:

java">public class MySingleLinkedList {static class ListNode {public ListNode next;public int val;public ListNode prev;public ListNode(int val) {this.val = val;}}public ListNode head; // 头节点 🧑‍💻public ListNode last; // 尾节点 🎯
}

讲解 🧑‍🏫

  • 每个节点由 ListNode 类表示,包含数据域 val 和两个指针域 nextprev
  • head 用于标记链表的起始位置,last 标记链表的尾节点(可选)。
  • 相较于数组,链表在插入和删除操作中更具灵活性,但随机访问性能较差。链表的插入和删除操作无需移动其他元素,且通过指针的变动即可实现动态扩展,极大提高了操作效率。

链表的基本操作 🔧

1. 链表的创建 🛠️

手动链接节点的方式可以快速构建一个简单的链表。我们通过手动指定每个节点的连接方式,构造一个简单的链表。它适用于学习和理解链表基本结构:

java">public void createList() {ListNode listNode1 = new ListNode(1);ListNode listNode2 = new ListNode(2);ListNode listNode3 = new ListNode(3);ListNode listNode4 = new ListNode(4);ListNode listNode5 = new ListNode(5);this.head = listNode1;listNode1.next = listNode2;listNode2.next = listNode3;listNode3.next = listNode4;listNode4.next = listNode5;listNode5.next = null;
}
动态创建链表 💻

动态创建链表可以根据输入动态生成节点并构建链表,这样不仅增加了代码的灵活性,而且便于应对不同长度和结构的链表需求:

java">public void createListDynamic(int[] values) {if (values == null || values.length == 0) {return;}this.head = new ListNode(values[0]);ListNode cur = this.head;for (int i = 1; i < values.length; i++) {cur.next = new ListNode(values[i]);cur = cur.next;}
}

讲解 💡

  • 动态连接节点node1.next = node2 是构建链表的核心逻辑,通过该步骤,node1next 指向了 node2,形成了节点之间的连接,链表的扩展就是通过这种方式完成的。🎯这种方式也适用于链表的动态增长,在需要时可以随时添加新的节点。
  • 灵活性:动态方法使得链表的创建不再依赖固定的结构,可以根据用户的输入或其他需求动态生成节点,极大提高了代码的通用性。

2. 链表的遍历 🔍

通过遍历链表打印节点值,同时考虑链表为空时的情况,避免出现空指针异常。遍历链表时,需要从头节点开始依次访问每个节点:

java">public void display() {if (head == null) {System.out.println("链表为空");return;}ListNode cur = head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();
}

讲解 📚

  • 动态连接节点node1.next = node2 是构建链表、插入节点的基础,解决了链表动态扩展问题。
  • 安全遍历链表while (cur != null) 确保链表遍历过程可靠且高效,避免了指针错误或无限循环的问题。这样可以避免遍历时访问空指针或进入无限循环。⚠️
  • 前进推进的流畅性cur = cur.next链表遍历的核心语句,它使得 cur 从当前节点指向下一个节点,实现了链表的逐步推进。这个简洁的推进逻辑符合链表的递归结构特性,也使得代码更易于理解。😊

3. 获取链表长度 🔢

链表的长度可以通过遍历链表的所有节点来计算,直到达到链表的末尾:

java">public int size() {ListNode cur = head;int length = 0;while (cur != null) {length++;cur = cur.next;}return length;
}

讲解 🧐

  • 递增计数:每遍历一个节点,计数器 length 加 1,直至链表末尾,遍历过程确保了准确的节点统计。📊
  • 优化建议:可以增加一个 size 属性,在链表增删时动态更新,避免每次都需要遍历整个链表来计算长度。

插入操作

1. 头插法 🏁

java">public void addFirst(int val) {ListNode listNode = new ListNode(val);listNode.next = head;head = listNode;
}

讲解

  • 直接插入头部:将新节点的 next 指向当前头节点,并更新 head 指针,使新节点成为链表的头节点。这种方式的时间复杂度为 O(1),是最为高效的插入方法,尤其适用于栈结构等需要优先级插入的场景。🔝

2. 尾插法 🔚

java">public void addLast(int val) {ListNode listNode = new ListNode(val);if (head == null) {head = listNode;return;}ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = listNode;
}

讲解 📈

  • 遍历尾部:通过遍历找到尾节点,将其 next 指向新节点。尾插法的时间复杂度是 O(n),如果链表较长,效率较低。⏳
  • 优化建议:通过维护尾指针 last,可以直接将新节点插入尾部,避免每次遍历链表,提升效率。⚙️

3. 任意位置插入 🔄

java">public void addIndex(int index, int val) {if (index < 0 || index > this.size()) {System.out.println("索引不合法");return;} else if (index == 0) {this.addFirst(val);return;} else if (index == this.size()) {this.addLast(val);return;} else {ListNode cur = findIndexSubOne(index);ListNode node = new ListNode(val);node.next = cur.next;cur.next = node;}
}private ListNode findIndexSubOne(int index) {int count = 0;ListNode cur = head;while (count != index - 1) {cur = cur.next;count++;}return cur;
}

讲解 📘

  • 定位前驱节点:通过 findIndexSubOne 方法找到目标索引的前驱节点,便于在目标位置进行插入。
  • 边界处理:针对索引为 0 或链表尾部的情况,分别调用头插法或尾插法。对其他位置的插入需要找到前驱节点,并调整指针,使得新节点被插入到正确的位置。🎯

删除操作

1. 删除第一个匹配的节点

java">public void remove(int val) {if (head == null) return;if (head.val == val) {head = head.next;return;}ListNode cur = head;while (cur.next != null) {if (cur.next.val == val) {cur.next = cur.next.next;return;}cur = cur.next;}
}

讲解 🧠

  • 遍历匹配:从头开始遍历链表,查找目标值所在节点。找到匹配节点后,通过调整前后节点的 next 指针,实现删除。
  • 特殊情况:若目标值位于头节点,则直接更新 head 指针,避免访问无效的 null 指针。⚠️

2. 删除所有匹配的节点

java">public void removeAllKey(int key) {ListNode cur = head;while (cur != null) {if (cur.val == key) {if (cur == head) {head = head.next;} else {cur.prev.next = cur.next;if (cur.next != null) {cur.next.prev = cur.prev;}}}cur = cur.next;}
}

讲解 🔍

  • 批量删除:遍历链表并删除所有匹配的节点。
  • 双向链表的优势:通过 prev 指针更方便地更新节点关系,实现更高效的删除操作。⚡

3. 清空链表

java">public void clear() {ListNode cur = head;while (cur != null) {ListNode next = cur.next;cur.next = null;cur.prev = null;cur = next;}head = last = null;
}

讲解 💬

  • 逐一断开连接:通过遍历依次清除每个节点的引用,确保没有悬挂引用。
  • 释放内存:同时将头尾指针设为 null,彻底清空链表。🚀

总结 🌟

通过本文的讲解,我们实现了以下操作:

  1. 链表的创建和遍历。
  2. 插入操作:头插、尾插和任意位置插入。
  3. 删除操作:删除单个节点、删除所有匹配节点,以及清空链表

链表作为基础数据结构,适用于动态插入和删除频繁的场景,但其随机访问性能较差。由于链表无法通过索引直接访问特定位置的节点,需要从头节点开始逐一遍历,因此在随机访问频繁的场景中可能不如数组高效。在实际开发中,链表的灵活性可以解决许多复杂的数据管理问题。希望这篇文章能帮助您更好地理解单链表的实现,也为后续学习更复杂的链表结构(如双向链表和循环链表)打下基础!😊🎉


http://www.ppmy.cn/server/161011.html

相关文章

【mybatis】一对多查询

书接上文&#xff0c;当多表查询&#xff0c;为了一个查询出全部&#xff0c;使用了GROUP_CONCAT和json_object函数&#xff0c;但是当数据量过大时&#xff0c;需要更改mysql的数据库配置&#xff0c;调大group_concat_max_len配置参数。 但是项目中可能存在线上数据库不让重…

高质量编程 性能优化学习笔记

高质量编程 & 性能优化学习笔记 目录 高质量编程 编程原则编码规范 性能优化 性能优化建议实战pprof性能调优案例 自动内存管理 常见内存管理方式 Go内存管理及优化 Go内存分配Go内存管理优化 编译器和静态分析 编译器静态分析 Go编译器优化 函数内联逃逸分析…

深入探索 Nginx 的高级用法:解锁 Web 服务器的强大潜能

在当下互联网技术飞速发展的浪潮中&#xff0c;Nginx 凭借其轻量级、高性能的特性&#xff0c;在 Web 服务器和反向代理服务器领域脱颖而出&#xff0c;成为众多开发者和运维工程师的得力工具。它不仅能高效处理静态资源&#xff0c;在负载均衡、反向代理等方面也表现出色。然而…

深入探索Python人脸识别技术:从原理到实践

一、引言在当今数字化时代,人脸识别技术已然成为了计算机视觉领域的璀璨明星,广泛且深入地融入到我们生活的各个角落。从门禁系统的安全守护,到金融支付的便捷认证,再到安防监控的敏锐洞察,它的身影无处不在,以其高效、精准的特性,极大地提升了我们生活的便利性与安全性…

靠右行驶数学建模分析(2014MCM美赛A题)

笔记 题目 要求分析&#xff1a; 比较规则的性能&#xff0c;分为light和heavy两种情况&#xff0c;性能指的是 a.流量与安全 b. 速度限制等分析左侧驾驶分析智能系统 论文 参考论文 两类规则分析 靠右行驶&#xff08;第一条&#xff09;2. 无限制&#xff08;去掉了第一条…

Ubuntu如何安装redis服务?

环境&#xff1a; Ubuntu22.04 WSL2 问题描述&#xff1a; 如何安装redis服务&#xff1f; 解决方案&#xff1a; 1.在 Linux 上&#xff08;如 Ubuntu/Debian&#xff09;安装 1.通过包管理工具安装 Redis 服务器&#xff1a; sudo apt update sudo apt install redis…

LeetCode:37. 解数独

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;37. 解数独 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff…

智慧农业——温湿,土壤,风速风向,降雨量 传感器监视平台

基于温湿、土壤、风速风向、降雨量传感器的智慧农业监视平台具有多方面的优点&#xff0c;主要体现在精准监测、智能决策、提升产量与品质、降低成本与风险等方面&#xff0c;以下是具体介绍&#xff1a; - **精准监测方面** - **实时数据获取**&#xff1a;能够实时采集农…