JavaScript的数据结构与算法(四) —— 双向链表

链表

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本事的节点和一个指向下一个元素的引用组成。相对于传统的数组,链表的一个好处在于,添加或者删除元素的时候不需要移动其他元素。
使用链表结构可以克服数组需要预先知道数据大小的缺点(C语言的数组需要预先定义长度),链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

数组和链表的一个不同在于数组可以直接访问任何位置的元素,而想要访问链表中的一个元素,需要从起点开始迭代列表。

链表又包括:单向链表 和 双向链表;

双向链表

双向链表与单向链表很是相像。在单向链表中,只有指向下一个节点的链接。但在双向链表中,还有指向上一个节点的链接,是双向的。

《JavaScript的数据结构与算法(四) —— 双向链表》

让我们来简单实现一个双向链表类,并包含如下功能:

  • append(element): 添加元素到链表尾部
  • insert(position,element): 向双向链表中某个位置插入元素
  • removeAt(position): 移除双向链表中某个位置的元素
  • getHead(): 获取双向链表的头部
  • getTail(): 获取双向链表的尾部
  • isEmpty(): 检查双向链表是否为空,为空则返回true
  • size(): 返回双向链表长度

代码如下:

   function DoublyLinkedList() {
    /**
     * 双向链表中节点的构造函数
     * 
     * @param {any} element 要插入链表的元素
     */
    var Node = function (element) {
        this.element = element;
        this.next = null;
        this.prev = null;
    }

    // 双向链表的长度
    var length = 0;
    // 双向链表的头结点,默认为null
    var head = null;
    // 双向链表的尾结点,默认为null
    var tail = null;
    
    /**
     * 添加元素到双向链表的尾部
     * 
     * @param {any} element 要插入的元素
     */
    this.append = function (element) {
        var node = new Node(element),
            current = head;

        if (head === null) {
            head = node
            tail = node
        } else {
            while (current.next) {
                current = current.next;
            }
            current.next = node;
            node.prev = current;
            tail = node;
        }
        length++;
        return true;
    }


    /**
     * 向双向链表中某个位置插入元素
     * 
     * @param {any} position 要插入的位置
     * @param {any} element 要插入的元素
     * @returns 插入成功或失败
     */
    this.insert = function (position, element) {
        var node = new Node(element),
            current = head,
            previous,
            index = 0;
        // 限制边界
        if (position < 0 && position >= length) {
            return false;
        }

        if (position === 0) {
            // 在链表的头部插入元素
            node.next = head
            head.prev = node
            head = node
        } else if (position === length) {
            // 在链表的尾部插入元素
            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;
            // 绑定后一个节点和插入节点的关系
            node.next = current;
            current.prev = node;
        }
        length++;
        return true;
    }

    /**
     * 移除双向链表中某个位置的元素
     * 
     * @param {any} position 要移除元素的位置
     * @returns 移除成功,返回移除的元素
     */
    this.removeAt = function (position) {
        var previous,
            current = head,
            index = 0;
         // 限制边界
         if (position < 0 && position >= length) {
            return false;
        }

        if (position === 0) {
            // 移除链表的头部
            head = current.next;
            head.prev = null;
        } else if(position === length - 1) {
             // 移除链表尾部的元素
             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;
    }

    this.getHead = function () {
        return head.element;
    }

    this.isEmpty = function () {
        return length === 0
    }

    this.getTail = function () {
        
        return tail.element;
    }

    this.size = function () {
        return length
    }
}
    原文作者:an_l
    原文地址: https://segmentfault.com/a/1190000011693321
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞