基础数据结构 -- 链表(Linked list)

目录

1. 简介

2. 图解

2.1 单向链表

2.2 双向链表

3. 应用

3.1 链表插入

3.1.1 在已知节点后插入

3.1.2 在尾部插入

3.2 LinkedList细节

4. 总结


1. 简介

链表是一种常用的数据结构,其特点是数据的线性排列和动态的存储分配

2. 图解

链表可分为单向链表和双向链表:

2.1 单向链表

每个节点包含一个指向下一个节点的指针,类似美国男篮队员之间的传递,一代接着一代。

public class Node {
    public int value;
    public Node next;

    public Node(int v){
        value = v;
    }
}

2.2 双向链表

每个节点不仅有指向下一个节点的指针,还有指向前一个节点的指针,像中国男篮队员之间既有向前的传球也有向后的回传。

public class DoubleNode {
    public int value;
    public DoubleNode pre;
    public DoubleNode next;

    public DoubleNode (int v){
        value = v;
    }
}

3. 应用

每说到链表,就会与数组进行对比。

在Java中,LinkedList(链表)ArrayList(数组),都是十分常用的数据容器。

相较于ArrayList,

  • 链表更适用于需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作
  • 如果需要频繁地访问列表元素还是ArrayList好用,毕竟LinkedList需要通过循环迭代来访问列表中的某些元素。

3.1 链表插入

插入和删除时链表的一大亮点。

相较于数组,数组最大的缺点就是,当我们在插入和删除时需要移动大量的元素。

链表的插入操作通常涉及以下几个步骤

  1. 定位插入点:确定在链表的哪个位置插入新节点。这可能需要遍历链表直到找到合适的位置。
  2. 创建新节点:分配一个新的节点,并将要插入的数据赋给这个新节点的数据部分。
  3. 修改指针:如果插入点是链表的开始,新节点的指针将指向原链表的第一个节点,同时链表的头指针需要更新为指向新节点。如果插入点是链表中间或末尾,需要修改前后两个节点的指针,使新节点被正确链接到链表中。
  4. 释放内存(可选):如果新节点是动态分配的内存,当不再需要这个节点时,应确保释放其占用的内存。

注:在一些编程语言中,如Java和C#等,内存管理是自动进行的,程序员不需要手动释放内存。但在C和C++等语言中,需要程序员显式地释放动态分配的内存。

   

3.1.1 在已知节点后插入

public class Node {
    public int value;
    public Node next;

    public Node(int v){
        value = v;
    }
}

public class MyLinkedList {
    Node head; // 链表的头节点

   public void insertAfter(Node preNode, int newData) {
        if (preNode == null) {
            return;
        }

        // 创建新节点
        Node newNode = new Node(newData);
        // 将新节点插入
        newNode.next = preNode.next;
        preNode.next = newNode;
    }
}
3.1.2 在尾部插入
public class Node {
    public int value;
    public Node next;

    public Node(int v){
        value = v;
    }
}

public class LinkedList {
    // 链表的头节点
    Node head; 

    // 在链表末尾添加一个新节点
    public void add(int data) {
        // 创建新节点
        Node newNode = new Node(); 
        newNode.value = data; 
        newNode.next = null; // 新节点是最后一个节点,所以next为null

        if (head == null) { 
            // 如果链表为空,新节点就是头节点
            head = newNode;
        } else { 
            // 找到链表的最后一个节点
            Node last = head;
            while (last.next != null) {
                last = last.next;
            }
            last.next = newNode; // 将新节点链接到最后一个节点
        }
    }
}

3.2 LinkedList细节

LinkedList 继承了 AbstractSequentialList 类,支持迭代器Iterator。

由于LinkedList不支持 RandomAccess,通过 迭代器遍历或增强for 会比 普通的for循环+get方法效率要高。所以遍历链表时,建议使用迭代器或者增强型的for循环。

LinkedList 实现了 Deque 接口,可作为队列使用。

LinkedList 实现了 List 接口,可进行列表的相关操作。

LinkedList 实现了 Cloneable 接口,可实现克隆。

LinkedList 实现了Serializable 接口,可支持序列化,能通过序列化去传输。

4. 总结

        链表是一种由一系列节点组成,每个节点包含数据和指向下一个节点的指针的数据结构。

  1. 优点:与数组相比,链表的优势在于能够动态地添加和删除元素,不需要预先分配固定大小的空间。
  2. 缺点:链表的内存开销比数组大,因为它需要额外的空间来存储指针。此外,链表的随机访问性能较差,因为必须从头部开始逐个遍历节点才能访问到目标元素,以及占用更多的内存空间来存储指针信息。
  3. 实际应用:它们在处理需要频繁插入删除操作的场景下特别有用。
  4. 实现细节:链表的增删改查操作通常需要遍历链表来完成,而在有虚拟头节点的情况下,可以简化一些操作的实现。虚拟头节点是一个不存储数据的节点,它的作用是为了统一操作逻辑,特别是在处理头节点和尾节点时。

         学习链表不仅是为了掌握这一数据结构本身,还有助于加深对数据结构和算法设计原则的理解,提高解决实际问题的能力。

  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值