Java中双链表的学习

300 阅读5分钟

双向链表

基础

  1. 双向链表是一种数据结构,它由一系列节点组成,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。
  2. 双向链表可以在任何位置添加、删除和修改节点,因为每个节点都可以直接访问前一个和后一个节点。
  3. 双向链表的头节点和尾节点分别是第一个节点和最后一个节点,它们都没有前驱和后继节点。
  4. 双向链表的每个节点通常包含两个部分:数据部分和指针部分。数据部分用于存储节点的值,指针部分用于存储前驱和后继节点的地址。
  5. 双向链表的实现需要定义一个节点类,包含节点值、前驱指针和后继指针等属性。同时,需要定义一个双向链表类,包含头节点和尾节点等属性,以及一系列方法来实现双向链表的各种操作。
  6. 双向链表在Java中有多种实现方式,例如LinkedList类就是使用双向链表实现的。在实际开发中,需要根据具体的需求来选择适当的数据结构。

代码实现双向链表的增删改查

public class DoubleNode {
    int val;
    DoubleNode prev;
    DoubleNode next;
    public DoubleNode(int val) {
        this.val = val;
        this.prev = null;
        this.next = null;
    }
}

public class DoubleLinkedList {
    private DoubleNode head;
    private DoubleNode tail;
    private int size;

    public DoubleLinkedList() {
        head = null;
        tail = null;
        size = 0;
    }

    public void addFirst(int val) {
        DoubleNode node = new DoubleNode(val);
        if (head == null) {
            head = tail = node;
        } else {
            node.next = head;
            head.prev = node;
            head = node;
        }
        size++;
    }

    public void addLast(int val) {
        DoubleNode node = new DoubleNode(val);
        if (tail == null) {
            head = tail = node;
        } else {
            tail.next = node;
            node.prev = tail;
            tail = node;
        }
        size++;
    }

    public void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;

        if (index == 0) {
            addFirst(val);
            return;
        }

        if (index == size) {
            addLast(val);
            return;
        }

        DoubleNode cur = head;
        for (int i = 0; i < index - 1; i++) {
            cur = cur.next;
        }
        DoubleNode node = new DoubleNode(val);
        node.prev = cur;
        node.next = cur.next;
        cur.next.prev = node;
        cur.next = node;

        size++;
    }

    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;

        if (index == 0) {
            head = head.next;
            if (head != null) head.prev = null;

            if (head == null) tail = null;

            size--;
            return;
        }

        if (index == size - 1) {
            tail = tail.prev;
            tail.next.prev = null;
            tail.next = null;

            if (tail == null) head = null;

            size--;
            return;
        }

        DoubleNode cur = head;

        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }

        cur.prev.next = cur.next;
        cur.next.prev = cur.prev;

        size--;
    }

    public int get(int index) {
        if (index < 0 || index >= size) return -1;

        if (index < size / 2) {
            DoubleNode cur = head;

            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }
            return cur.val;

        } else {

            DoubleNode cur = tail;

            for (int i = size - 1; i > index; i--) {
                cur = cur.prev;
            }
            return cur.val;

        }
    }
}

定义了一个双向链表的节点类DoubleNode,包括节点的值val和前节点prev和后节点next。然后定义了一个双向链表类DoubleLinkedList,包括头结点head、尾结点tail和链表长度size。这个类实现了头插法addFirst、尾插法addLast、在指定位置插入元素addAtIndex、删除指定位置元素deleteAtIndex和获取指定位置元素get等操作。

优缺点

双向链表是一种数据结构,它与单向链表不同,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。

优点:

  1. 可以在任意位置快速添加、删除和修改节点,因为每个节点都可以直接访问前一个和后一个节点,不需要像单向链表那样遍历到需要操作的节点的前一个节点。
  2. 双向链表可以从前向后或从后向前遍历整个链表,这使得某些特定场景下的操作更加方便,例如需要从链表尾部开始遍历或倒序输出链表中的节点。

缺点:

  1. 双向链表需要额外的指针空间来存储前驱和后继节点,因此占用的存储空间较大,比单向链表更加浪费空间。
  2. 双向链表的每个节点都需要维护前驱和后继指针,因此在插入、删除和修改节点时需要更新相应的指针,这会增加一定的时间和空间复杂度。
  3. 双向链表的实现相对于单向链表的实现要复杂一些,需要维护额外的指针,并且容易出现指针问题,例如指针错误、空指针异常等,对于开发和调试都带来一定的难度。

单双链表的区别

  1. 节点结构不同

单链表的节点结构包含两个部分:数据部分和指针部分,指针部分只包含指向后继节点的指针,不包含指向前驱节点的指针。

双链表的节点结构包含三个部分:数据部分和两个指针部分,一个指向前驱节点,一个指向后继节点。

  1. 遍历方向不同

在单链表中,只能从头节点开始遍历整个链表,每个节点只能访问它的后继节点,无法访问它的前驱节点。

在双链表中,可以从任意节点开始遍历整个链表,每个节点既可以访问它的前驱节点,也可以访问它的后继节点。

  1. 操作复杂度不同

在单链表中,插入和删除节点需要找到目标节点的前驱节点,需要遍历整个链表,时间复杂度为O(n)。而在双链表中,可以直接访问目标节点的前驱和后继节点,因此插入和删除节点的时间复杂度为O(1)。

  1. 存储空间不同

由于单链表每个节点只包含一个指针,因此占用的存储空间比双链表要小一半。而双链表每个节点需要额外的指向前驱节点的指针,因此占用的存储空间比单链表要大一些。

总的来说,单链表和双链表都有其优缺点,应根据具体情况选择合适的数据结构。单链表空间占用小,但插入和删除节点的复杂度较高;双链表插入和删除节点的复杂度低,但占用的存储空间相对较大。