双向链表的实现(Java)

4,720 阅读3分钟

一、双向链表介绍

双向链表,其实就是链表的每个结点都可以知道自己的前一个结点和后一个结点。

每个结点都有一个前驱指针和后驱指针,分别存储前一个结点和后一个结点在内存中的地址。

head 指向链表第一个有效结点。

二、双向链表的实现

1、插入

双向链表的插入操作,只需要改变相邻结点的指针指向就可以。时间复杂度为 O(1)。

    /**
     * 将新的节点插入到指定节点后
     *
     * @param preNode 指定节点
     * @param newNode 新的节点
     */
    public void add(SNode<T> preNode, SNode<T> newNode) {
        // 要插入到链表末尾时,不需要维护下一个结点的前驱指针
        if (preNode.next != null) {
            preNode.next.pre = newNode;
        }
        newNode.next = preNode.next;
        newNode.pre = preNode;
        preNode.next = newNode;
        size++;
    }

2、删除

双向链表的删除和其他删除操作不一样,只要知道要删除的结点,就可以直接删除,而不需要找要删除的结点的前一个结点。时间复杂度为 O(1)。

    /**
     * 删除数据域为指定值的元素
     *
     * @param e
     */
    public void del(T e) {
        SNode<T> temp = head;
        while (temp != null) {
            if (temp.data.equals(e)) {
                // 维护 head,head 永远指向双向链表第一个有效结点
                temp.next.pre = temp.pre;
                if (temp == head) {
                    head = head.next;
                    head.pre = null;
                } else {
                    temp.pre.next = temp.next;
                }
                return;
            }
            // temp 向后移
            temp = temp.next;
        }
    }

三、总结

双向链表是链表的一种,它的每个结点都有一个前驱和后驱。分别存储前驱前一个结点和后一个结点在内存中的地址。插入和删除的时间复杂度都是 O(1)。

四、完整代码

/**
 * 双向链表的实现
 *
 * @author liyanan
 * @date 2020/1/2 13:16
 */
public class DoubleLinkedList<T> {
    public static class SNode<T> {
        /**
         * 存储数据
         */
        public T data;
        /**
         * 指向当前结点的前一个结点
         */
        public SNode<T> pre;
        /**
         * 指向当前结点的下一个节点
         */
        public SNode<T> next;

        public SNode() {
        }

        public SNode(T data) {
            this.data = data;
        }
    }

    /**
     * 双向链表的头结点,存储第一个有效结点的基地址
     */
    private SNode<T> head;

    /**
     * 双向链表的有效结点数量
     */
    private int size;

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

    public int getSize() {
        return size;
    }


    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 插入结点到双向链表末尾
     *
     * @param newNode
     */
    public void addLast(SNode<T> newNode) {
        if (isEmpty()) {
            head = newNode;
            head.next = null;
            head.pre = null;
            size++;
        } else {
            SNode<T> temp = head;
            // 找到双向链表最后一个有效结点
            while (temp.next != null) {
                temp = temp.next;
            }
            // 将新结点加入双向链表
            add(temp, newNode);
        }
    }

    /**
     * 将新的节点插入到指定节点后
     *
     * @param preNode 指定节点
     * @param newNode 新的节点
     */
    public void add(SNode<T> preNode, SNode<T> newNode) {
        // 要插入到链表末尾时,不需要维护下一个结点的前驱指针
        if (preNode.next != null) {
            preNode.next.pre = newNode;
        }
        newNode.next = preNode.next;
        newNode.pre = preNode;
        preNode.next = newNode;
        size++;
    }

    /**
     * 删除数据域为指定值的元素
     *
     * @param e
     */
    public void del(T e) {
        SNode<T> temp = head;
        while (temp != null) {
            if (temp.data.equals(e)) {
                // 维护 head,head 永远指向双向链表第一个有效结点
                temp.next.pre = temp.pre;
                if (temp == head) {
                    head = head.next;
                    head.pre = null;
                } else {
                    temp.pre.next = temp.next;
                }
                return;
            }
            // temp 向后移
            temp = temp.next;
        }
    }

    /**
     * 删除指定位置的结点
     *
     * @param k
     */
    public void del(int k) {
        SNode<T> delNode = find(k);
        delNode.next.pre = delNode.pre;
        if (delNode == head) {
            head = head.next;
            head.pre = null;
        } else {
            delNode.pre.next = delNode.next.next;
        }
    }

    /**
     * 找到双向链表第 k 个结点
     *
     * @param k k 从 0 开始
     * @return
     */
    public SNode<T> find(int k) {
        if (k > size || k < 0) {
            throw new RuntimeException("传入的参数 k 必须大于等于零并且小于等于链表的长度 size");
        }
        int cnt = 0;
        SNode<T> t = head;
        while (cnt != k) {
            cnt++;
            t = t.next;
        }
        return t;
    }

    /**
     * 打印单链表所有有效节点
     *
     * @return
     */
    public String printAll() {
        StringBuilder sb = new StringBuilder();
        SNode<T> temp = head;
        while (temp != null) {
            sb.append(temp.data);
            sb.append(" ");
            temp = temp.next;
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        DoubleLinkedList<Integer> doubleLinkedList = new DoubleLinkedList<>();
        SNode<Integer> node1 = new SNode<>(1);
        SNode<Integer> node2 = new SNode<>(2);
        SNode<Integer> node3 = new SNode<>(3);
        SNode<Integer> node4 = new SNode<>(4);
        SNode<Integer> node5 = new SNode<>(5);
        doubleLinkedList.addLast(node1);
        doubleLinkedList.addLast(node2);
        doubleLinkedList.addLast(node3);
        doubleLinkedList.addLast(node4);
        doubleLinkedList.addLast(node5);
        System.out.println("插入五个结点:");
        System.out.println(doubleLinkedList.printAll());

        System.out.println("在第四个结点后插入一个新结点:");
        SNode<Integer> node6 = new SNode<Integer>(6);
        doubleLinkedList.add(node4, node6);
        System.out.println(doubleLinkedList.printAll());
        System.out.println("在第 1 个结点后插入一个新结点:");
        SNode<Integer> node7 = new SNode<Integer>(7);
        doubleLinkedList.add(node1, node7);
        System.out.println(doubleLinkedList.printAll());

        System.out.println("删除值为 1 的结点:");
        doubleLinkedList.del(Integer.valueOf(1));
        System.out.println(doubleLinkedList.printAll());
        System.out.println("删除值为 3 的结点:");
        doubleLinkedList.del(Integer.valueOf(3));
        System.out.println(doubleLinkedList.printAll());
        System.out.println("删除值为 6 的结点:");
        doubleLinkedList.del(Integer.valueOf(6));
        System.out.println(doubleLinkedList.printAll());

        System.out.println("删除第一个结点");
        doubleLinkedList.del(0);
        System.out.println(doubleLinkedList.printAll());

    }
}