【数据结构】单链表核心知识点梳理

一、基本定义与结构

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。科普中国-单链表

1. 节点结构

单链表由节点串联而成,每个节点包含两部分:

  • 数据域:存储元素值(如整数、字符串等)
  • 指针域:通过引用(Java中)指向后继节点

Java节点类定义

java 复制代码
public class ListNode {
    int val; // 数据域
    ListNode next; // 指针域(指向后继节点)

    // 构造方法
    public ListNode(int val) {
        this.val = val;
        this.next = null; // 初始后继节点为null
    }
    
    // get、set方法
    public int getVal() {
        return val;
    }
    
    public void setVal(int val) {
        this.val = val;
    }
    
    public ListNode getNext() {
        return next;
    }
    
    public void setNext(ListNode next) {
        this.next = next;
    }
    
}

2. 头节点的作用

  • 带头节点 :头节点不存储实际数据,其next指向首节点(第一个数据节点)。优势是统一插入/删除操作逻辑(无需特殊处理首节点),算法题中常用。

    java 复制代码
    // 带头节点的链表初始化
    ListNode head = new ListNode(-1); // 头节点(值无实际意义)
  • 不带头节点:首节点即第一个数据节点,操作首节点时需单独处理(如插入首节点需修改头指针),易出错,实际应用中较少使用。

二、创建方式

1. 头插法

  • 核心逻辑 :新节点的next先指向头节点的后继,再将头节点的next指向新节点。
  • 特性 :链表元素顺序与插入顺序相反(逆序),可直接用于链表逆置。

Java示例

java 复制代码
    public static ListNode createByHeadInsert(int[] elements) {
        ListNode head = new ListNode(-1); // 头节点
        for (int val : elements) {
            ListNode newNode = new ListNode(val);
            newNode.setNext(head.getNext());
            head.setNext(newNode);
        }
        return head;
    }

数据流转过程如下:

  • 初始化头节点 :创建一个值为 -1 的头节点(哨兵节点),此时链表为空, head.getNext() 为 null

  • 遍历数组元素 :依次处理传入的每个元素值

  • 数据流转核心步骤 (以数组 [1,2,3] 为例):

  • 插入第一个元素 1 :

    • 创建新节点 newNode(1)
    • newNode.setNext(head.getNext()) → newNode.next = null
    • head.setNext(newNode) → head.next = newNode(1)
    • 链表状态:head(-1) -> 1 -> null
  • 插入第二个元素 2 :

    • 创建新节点 newNode(2)
    • newNode.setNext(head.getNext()) → newNode.next = 节点1
    • head.setNext(newNode) → head.next = newNode(2)
    • 链表状态:head(-1) -> 2 -> 1 -> null
  • 插入第三个元素 3 :

    • 创建新节点 newNode(3)
    • newNode.setNext(head.getNext()) → newNode.next = 节点2
    • head.setNext(newNode) → head.next = newNode(3)
    • 链表状态:head(-1) -> 3 -> 2 -> 1 -> null

2. 尾插法

  • 核心逻辑 :用尾指针tail跟踪当前尾节点,每次将新节点连接到tail后,更新tail指向新节点。
  • 特性 :链表元素顺序与插入顺序一致 (正序)。

Java示例

java 复制代码
    public static ListNode createByTailInsert(int[] elements) {
        ListNode head = new ListNode(-1); // 头节点
        ListNode tail = head; // 尾指针初始指向头节点
        for (int val : elements) {
            ListNode newNode = new ListNode(val);
            tail.setNext(newNode);
            tail = newNode;
        }
        tail.setNext(null);
        return head;
    }
  • 初始化阶段 :

    • 创建一个值为 -1 的头节点(哨兵节点)
    • 将尾指针 tail 初始指向头节点
    • 此时链表为空, head.getNext() 和 tail.getNext() 都为 null
  • 遍历数组元素 :依次处理传入的每个元素值

  • 数据流转核心步骤 (以数组 [1,2,3] 为例):

    • 插入第一个元素 1 :

      • 创建新节点 newNode(1)
      • tail.setNext(newNode) → 此时 tail 指向 head,所以 head.next = newNode(1)
      • tail = newNode → 尾指针移动到新节点1
      • 链表状态:head(-1) -> 1 -> null
    • 插入第二个元素 2 :

      • 创建新节点 newNode(2)
      • tail.setNext(newNode) → 此时 tail 指向节点1,所以 1.next = newNode(2)
      • tail = newNode → 尾指针移动到新节点2
      • 链表状态:head(-1) -> 1 -> 2 -> null
    • 插入第三个元素 3 :

      • 创建新节点 newNode(3)
      • tail.setNext(newNode) → 此时 tail 指向节点2,所以 2.next = newNode(3)
      • tail = newNode → 尾指针移动到新节点3
      • 链表状态:head(-1) -> 1 -> 2 -> 3 -> null
  • 收尾处理 :

    • 设置尾节点的 next 为 null: tail.setNext(null)

三、基本操作

1. 插入操作

  • 场景 :在节点p后插入新节点newNode
  • 步骤 (顺序不可颠倒):
    1. newNode.next = p.next(新节点先连接p的后继)// newNode.setNext(p.getNext());
    2. p.next = newNodep再指向新节点)// p.setNext(newNode);

Java示例

代码中用的是get,set方法(请看标题:1. 节点结构 中的内容),代码中没用.next什么的,get,set看着更舒服(我感觉)

java 复制代码
    public static void insertAfter(ListNode p, int val) {
        if (p == null) return;
        ListNode newNode = new ListNode(val);
        newNode.setNext(p.getNext());
        p.setNext(newNode);
    }

2. 删除操作

  • 场景 :删除节点p的后继节点。
  • 步骤
    1. 找到待删节点的前驱p
    2. p.next = p.next.nextp直接指向待删节点的后继,跳过待删节点)
    3. (Java中无需手动释放内存,由GC自动回收)


【鉴于个人视角的有限性,所述观点未必全面,欢迎理性探讨与指正】

节点2指向节点3的引用("2到3这条线")暂时依然存在,但由于节点2不再被链表中的任何其他节点引用,它变成了一个孤立节点

当JVM的垃圾回收器检测到节点2不再被任何活动引用指向时,会将其标记为可回收

最终在垃圾回收过程中,节点2占用的内存会被释放

节点2内部的所有引用(包括它指向节点3的引用)也会一并消失

所以最后2到3这条线没了

Java示例

代码中用的是getset方法(请看标题:1. 节点结构 中的内容)

java 复制代码
    public static void deleteAfter(ListNode p) {
        if (p == null || p.getNext() == null) return;
        p.setNext(p.getNext().getNext());
    }

本文内容仅供参考,不构成任何形式的专业建议。作者尽力确保信息的准确性,但不对因使用本文内容而引发的任何直接或间接损失负责。读者应自行核实信息并咨询相关专业人士。

相关推荐
一只老丸2 小时前
HOT100题打卡第36天——二分查找
数据结构·算法
潼心1412o2 小时前
数据结构(长期更新)第7讲:栈
数据结构
Fency咖啡3 小时前
redis进阶 - 底层数据结构
数据结构·数据库·redis
2201_757830873 小时前
泛型的细节
java·开发语言·数据结构
墨雪不会编程4 小时前
数据结构—排序算法篇三
数据结构·算法·排序算法
飞鱼&6 小时前
java数据结构
数据结构·二叉树·散列表·红黑树
有意义7 小时前
为什么说数组是 JavaScript 开发者必须精通的数据结构?
前端·数据结构·算法
努力努力再努力wz7 小时前
【Linux进阶系列】:线程(下)
linux·运维·服务器·c语言·数据结构·c++·算法