讲师中心 微信公众号

工具推荐

视频效率加速
Seko
Seko

AI短剧一键生成成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

updream

一键生成高质量推广成片

AniShort
AniShort

AI短剧协同制作平台

Java双向链表:实现高效的按索引删除节点操作

风伟姑娘_1748

风伟姑娘_1748

发布时间:2025-09-13 10:19:27

|

218人浏览过

|

来源于php中文网

原创

Java双向链表:实现高效的按索引删除节点操作

本文详细讲解了如何在Java中为双向链表实现按索引删除节点的操作。教程涵盖了泛型设计、节点结构、参数校验、以及针对头节点、尾节点和中间节点的删除逻辑,并强调了维护链表head、tail和size等状态的准确性,确保了删除操作的健壮性和正确性。

1. 双向链表节点与泛型设计

在实现双向链表时,为了提高代码的复用性和灵活性,我们通常会采用泛型设计。一个双向链表的节点不仅包含数据,还需要指向前一个节点和后一个节点的引用。

Node 类结构:

class Node<T> {
    T data; // 存储节点数据
    Node<T> next; // 指向下一个节点
    Node<T> previous; // 指向前一个节点

    public Node(T data) {
        this.data = data;
        this.next = null;
        this.previous = null;
    }

    @Override
    public String toString() {
        return data.toString();
    }
}

这里,Node 采用泛型 T 来存储数据,这使得我们的链表可以存储任何类型的数据。为了使链表中的元素具有可比性(虽然删除操作本身不直接需要,但链表操作如排序等场景会用到),通常要求泛型类型 T 实现 Comparable 接口。

Student 示例类:

class Student implements Comparable<Student> {
    private static int counter;
    private int id;

    public Student() {
        id = ++counter;
    }

    @Override
    public int compareTo(Student o) {
        return id - o.id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student other = (Student) obj;
        return id == other.id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public String toString() {
        return String.format("%d", id);
    }
}

Student 类实现了 Comparable 接口,并提供了 equals, hashCode, toString 方法,以满足泛型链表的基本要求和方便调试。

立即学习“Java免费学习笔记(深入)”;

2. 双向链表基础结构

DoublyLinkedList 类是双向链表的管理核心,它维护着链表的头部 (head)、尾部 (tail) 以及当前链表的大小 (size)。

logos-node
logos-node

在 Linux x86_64 上安装、更新或检查 Logos 区块链测试网验证节点。用于用户需要搭建 Logos 节点时使用。

下载
public class DoublyLinkedList<T extends Comparable<T>> {
    protected Node<T> head;
    protected Node<T> tail;
    int size = 0;

    public DoublyLinkedList() {
        this.head = null;
        this.tail = null;
    }

    /**
     * 在链表末尾添加一个新节点
     * @param data 要添加的数据
     * @return 新添加的节点
     */
    public Node<T> append(T data) {
        Node<T> newNode = new Node<>(data);
        if (head == null) { // 链表为空时
            head = newNode;
            tail = newNode;
        } else { // 链表不为空时
            newNode.previous = tail;
            tail.next = newNode;
            tail = newNode;
        }
        size++;
        return newNode;
    }

    // ... delete 方法将在这里实现 ...

    /**
     * 辅助方法:将链表内容转换为字符串,便于可视化
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("Size[%d]: ", size));
        Node<T> current = head;
        while (current != null) {
            sb.append(current);
            sb.append(" <-> "); // 使用 <-> 表示双向链接
            current = current.next;
        }
        if (sb.length() > "Size[0]: ".length()) { // 移除最后一个 <->
            sb.setLength(sb.length() - 5); 
        }
        return sb.toString();
    }
}

append 方法负责在链表末尾添加新节点,并正确更新 head、tail 和 size。toString 方法则提供了一个方便的链表内容展示方式。

3. 实现按索引删除节点 (delete方法)

删除操作是链表管理中较为复杂的部分,需要仔细处理各种边界条件和指针重定向。

public void delete(int location) throws IllegalArgumentException {
    // 3.1 参数校验
    // 检查索引是否合法:非负且小于链表大小
    if (head == null || location < 0 || location >= size) {
        throw new IllegalArgumentException("无效的索引或链表为空,无法删除节点。");
    }

    // 3.2 核心逻辑:分情况处理
    if (location == 0) { // 情况一:删除头节点
        head = head.next; // 头节点指向下一个节点
        if (head != null) {
            head.previous = null; // 新头节点的前驱设为null
        } else { // 如果链表只有一个节点,删除后链表为空
            tail = null;
        }
    } else if (location == size - 1) { // 情况二:删除尾节点
        tail = tail.previous; // 尾节点指向前一个节点
        if (tail != null) {
            tail.next = null; // 新尾节点的后继设为null
        } else { // 如果链表只有一个节点(此分支不会执行,因为location=0已处理)
            head = null;
        }
    } else { // 情况三:删除中间节点
        Node<T> current = head;
        // 遍历到要删除节点的前一个节点
        for (int i = 0; i < location - 1; i++) {
            current = current.next;
        }
        // current 现在是目标节点的前一个节点
        Node<T> nodeToDelete = current.next;
        Node<T> nextNode = nodeToDelete.next;

        current.next = nextNode; // 前一个节点的next指向目标节点的next
        if (nextNode != null) {
            nextNode.previous = current; // 目标节点的next(如果存在)的previous指向前一个节点
        }
    }
    size--; // 链表大小减一
}

delete 方法详解:

  • 3.1 参数校验: 首先,对传入的 location 参数进行严格校验。如果 location 小于 0 或大于等于链表当前 size,则表示索引无效,抛出 IllegalArgumentException。此外,如果链表本身为空 (head == null),也无法执行删除操作。
  • 3.2 核心逻辑:分情况处理
    • 情况一:删除头节点 (location == 0)
      • 将 head 指针直接移动到 head.next。
      • 如果新的 head 不为 null,则将其 previous 指针设为 null。
      • 特殊处理: 如果删除头节点后 head 变为 null(即原链表只有一个节点),则 tail 也必须设为 null,表示链表已空。
    • 情况二:删除尾节点 (location == size - 1)
      • 将 tail 指针移动到 tail.previous。
      • 如果新的 tail 不为 null,则将其 next 指针设为 null。
      • 此分支不会处理链表只剩一个节点的情况,因为 location == 0 已覆盖。
    • 情况三:删除中间节点 (0
      • 使用一个 current 指针从 head 开始遍历,直到找到要删除节点的前一个节点。
      • 一旦找到 current (即 location - 1 处的节点),我们就可以获取到 nodeToDelete = current.next 和 nextNode = nodeToDelete.next。
      • 重定向指针:
        • current.next = nextNode;:将 current 的 next 指针绕过 nodeToDelete,直接指向 nextNode。
        • if (nextNode != null) { nextNode.previous = current; }:如果 nextNode 存在,则将其 previous 指针指向 current,完成双向链接的修复。
  • 3.3 链表大小更新: 无论哪种情况,只要成功删除了节点,size 都必须减 1。

4. 辅助方法与示例

为了验证 delete 方法的正确性,我们可以添加一个 main 方法进行测试。

    public static void main(String[] args) {
        DoublyLinkedList<Student> dll = new DoublyLinkedList<>();
        System.out.println("初始链表: " + dll);

        // 添加节点
        dll.append(new Student()); // 1
        dll.append(new Student()); // 2
        dll.append(new Student()); // 3
        dll.append(new Student()); // 4
        System.out.println("添加4个节点后: " + dll); // Size[4]: 1 <-> 2 <-> 3 <-> 4

        // 测试删除操作
        System.out.println("\n--- 测试删除操作 ---");

        // 删除中间节点 (索引 1,即节点2)
        dll.delete(1); 
        System.out.println("删除索引1的节点后: " + dll); // Size[3]: 1 <-> 3 <-> 4

        // 删除头节点 (索引 0,即节点1)
        dll.delete(0);
        System.out.println("删除索引0的节点后: " + dll); // Size[2]: 3 <-> 4

        // 删除尾节点 (索引 size-1,即节点4)
        dll.delete(dll.size - 1);
        System.out.println("删除尾节点后: " + dll); // Size[1]: 3

        // 删除最后一个节点 (索引 0,即节点3)
        dll.delete(0);
        System.out.println("删除最后一个节点后: " + dll); // Size[0]: 

        // 尝试在空链表上删除
        try {
            dll.delete(0);
        } catch (IllegalArgumentException e) {
            System.out.println("尝试在空链表上删除(预期错误): " + e.getMessage());
        }

        // 重新添加节点,测试删除倒数第二个节点
        System.out.println("\n--- 测试删除倒数第二个节点 ---");
        dll.append(new Student()); // 5
        dll.append(new Student()); // 6
        dll.append(new Student()); // 7
        System.out.println("添加3个节点后: " + dll); // Size[3]: 5 <-> 6 <-> 7
        dll.delete(1); // 删除索引1的节点 (即节点6)
        System.out.println("删除索引1的节点后: " + dll); // Size[2]: 5 <-> 7

        // 验证 tail 是否正确
        System.out.println("当前 tail: " + (dll.tail != null ? dll.tail.data : "null")); // 应该是 7
    }
}

5. 注意事项与总结

  • 边界条件处理: 删除操作的复杂性主要体现在对边界条件(空链表、删除头节点、删除尾节点、链表只剩一个节点)的细致处理上。任何一个指针的错误指向都可能导致链表断裂或 NullPointerException。
  • 指针重定向: 双向链表在删除节点时,不仅要更新被删除节点的前一个节点的 next 指针,还要更新被删除节点的后一个节点的 previous 指针。
  • head 和 tail 的维护: 每次删除操作都可能影响 head 和 tail 指针,尤其是在删除头节点或尾节点,以及链表变为空时,必须确保这两个关键指针的正确性。
  • size 的更新: 及时更新链表的 size 成员变量对于维护链表状态和进行有效参数校验至关重要。
  • 泛型优势: 采用泛型设计使链表具有更好的通用性,能够存储和操作各种类型的数据。

通过上述详细的步骤和代码示例,我们可以实现一个健壮且高效的Java双向链表按索引删除节点的操作。理解并正确处理这些细节是掌握链表数据结构的关键。

热门AI工具

更多
码上飞
码上飞 Hot

一款基于自然语言的AI应用开发平台,可根据用户描述自动生成应用程序,帮助个人和团队快速完成应用原型与开发。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

379

2026.04.23

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2841

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

396

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

437

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

492

2026.05.09

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3228

2023.10.19

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

40

2026.11.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn