JavaScript数据结构之-双向链表

2,187 阅读2分钟

链表相对传统数组优点是:添加删除元素不会移动其它元素。链表相对传统数组缺点是:访问链表中间的元素,需要从头迭代,直到找到所需元素。上一篇文章讨论了链表,本章叙述双向链表。双向链表是双向的,一个链向上一个元素,一个链向上一个元素。 双向链表提供两种迭代方式:从头到尾和从尾到头,我们可以访问特定一个节点的上一个元素或者下一个元素。

简单链表和双向链表非常类似,上一章 juejin.cn/post/684490… 已经介绍了基本的操作方法和写法,这次介绍双向链表和简单链表不一样的一些算法。

在任意位置插入新元素

function DoublyLinkedList() {
    let Node = function(element) {
        this.element = element;
        this.next = null;
        this.prev = null;
    };
    let length = 0,
        head = null,
        tail = null;
    // 在任意位置插入新元素
    this.insert = function(position) {
        if (position >= 0 && position <= length) {
            let node = new Node(element),
                current = head,
                previous = null,
                index = 0;
            if (0 === position) {
                if (!head) {
                    head = node;
                    tail = node;
                } else {
                    node.next = current;
                    current.prev = node;
                    head = node;
                }
            } else if (length === position) {
                current = tail;
                current.next = node;
                node.prev = current;
                tail = node;
            } else {
                while (index++ < position) {
                    previous = current;
                    current = current.next;
                }
                previous.next = node;
                node.prev = previous;
                current.prev = node;
                node.next = current;
            }
            length++;
            return true;
        } else {
            return false;
        }
    };
}

插入大致分为三种场景:1、 在列表第一个位置插入元素,如果列表为空,head和tail都指向这个新节点。如果列表不为空,current将是对这个列表对一个元素的引用,node.next指向current,current.prev指向node。2、在列表最后插入一个元素,这是一种比较特殊的场景,因为最后还控制着tail指针,current指向最后一个指针的引用,然后建立里一个链接,current.next指向node,然后node.prev指向current,最后更新tail,指向node。3、在列表任意位置插入元素,首先遍历到指定位置,这里就需要在指定位置前后位新元素建立两两链接。这样才不会丢掉节点之间的关系。

从任意位置移除元素

function DoublyLinkedList() {
    let Node = function(element) {
        this.element = element;
        this.next = null;
        this.prev = null;
    };
    let length = 0,
        head = null,
        tail = null;
    // 从任意位置移除元素
    this.removeAt = function(position) {
        if (position > -1 && position < length) {
            let current = head,
                previous = null,
                index = 0;
            if (0 === position) {
                head = current.next;
                if (1 === length) {
                    tail = null;
                } else {
                    head.prev = null;
                }
            } else if (length - 1 === position) {
                current = tail;
                tail = current.prev;
                tail.next = null;
            } else {
                while (index++ < position) {
                    previous = current;
                    current = current.next;
                }
                previous.next = current.next;
                current.next.prev = previous;
            }
            length--;
            return current.element;
        } else {
            return null;
        }
    };
}

删除也大致分为三种场景:1、 在列表第一个位置删除元素。2、删除列表最后一个元素,这是一种比较特殊的场景,因为最后还控制着tail指针。3、在列表任意位置删除元素。

双向链表的删除和插入操作最基本的思想一致,在操作元素时保证元素间前后链接不能断开。

下一章:循环链表