链表

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。

链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表时需要额外注意。数组的另一个细节是可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,需要从起点开始迭代列表直到找到所需的元素。

创建链表

function LinkedList() {
  var Node = function (element) {
    this.element = element;
    this.next = null;
  };
  var length = 0;
  var head = null;

  this.append = function (element) {
    //向链表尾部添加元素
    var node = new Node(element);
    var current;
    if (head === null) {
      //当链表为空的时候,直接插入
      head = node;
    } else {
      current = head;
      while (current.next) {
        current = current.next; //找到当前链表的最后一项
      }
      current.next = node; //最后一项的next指针指向node
    }
    length++; //长度加一
  };

  this.removeAt = function (position) {
    //删除某一个位置的节点
    if (position > -1 && position < length) {
      //判断是否越界
      var current = head,
        previous,
        index = 0;
      if (position === 0) {
        //删除第一个节点
        head = current.next;
      } else {
        while (index < position) {
          //迭代链表
          previous = current;
          current = current.next;
          index++;
        }
        previous.next = current.next; //previous和current的后继节点连接,删除current
      }
      length--;
      return current.element;
    } else {
      return null;
    }
  };

  this.insert = function (position, element) {
    //可以在任意位置插入一个元素
    if (position > -1 && position < length) {
      var node = new Node(element),
        current = head,
        previous,
        index = 0;
      if (position === 0) {
        //判断插入位置是否为第一个
        node.next = current;
        head = node;
      } else {
        while (index < position) {
          //迭代链表
          previous = current;
          current = current.next;
          index++;
        }
        previous.next = node;
        node.next = current;
      }
      length++;
      return true;
    } else {
      return false;
    }
  };

  this.toString = function () {
    //链表按着字符串输出
    var current = head;
    var string = '';
    while (current) {
      //迭代链表
      string = current.element;
      current = current.next;
    }
    return string;
  };

  this.indexOf = function (element) {
    //查找该值是否在链表中存在
    var current = head;
    var index = -1;
    while (current) {
      //迭代链表
      if (current.element === element) {
        //找到,则返回当前索引
        return index;
      }
      index++;
      current = current.next;
    }
    return -1;
  };

  this.remove = function (element) {
    //删除某个值
    var index = this.indexOf(element); //查找到当前值的索引
    return this.removeAt(index); //按位置删除当前值
  };

  this.isEmpty = function () {
    //判断当前链表是否为空
    return length === 0;
  };

  this.size = function () {
    //判断当前链表的大小
    return length;
  };

  this.getHead = function () {
    //返回链表头部
    return head;
  };
}

双向链表

双向链表和普通链表的区别在于,在链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素。

双向链表提供了两种迭代列表的方法:从头到尾,或者反过来。我们也可以访问一个特定节点的下一个或前一个元素。

链表的实现:

function DoublyLinkedList() {
  var Node = function (element) {
    this.element = element;
    this.prev = null; //前继节点
    this.next = null; //后继节点
  };
  var length = 0;
  var head = null;
  var tail = null; //用来保存对列表最后一项的引用的tail属性

  this.insert = function (position, element) {
    //插入节点
    if (position >= 0 && position <= length) {
      //检查是否越界
      var node = new Node(element);
      var current = head,
        previous,
        index = 0;
      if (position === 0) {
        //如果是在第一个位置插入
        if (!head) {
          //如果当前链表为空
          head = node;
          tail = node;
        } else {
          node.next = current;
          current.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;
          index++;
        }
        previous.next = node;
        node.prev = previous;
        node.next = current;
        current.prev = node;
      }
      length++;
      return true;
    } else {
      return false;
    }
  };

  this.removeAt = function (position) {
    //删除任意位置的节点
    if (position >= 0 && position <= length) {
      //检查是否越界
      var current = head,
        previous,
        index = 0;
      if (position === 0) {
        //删除第一位
        head = current.next;
        if (length === 1) {
          tail = null;
        } else {
          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;
          index++;
        }
        previous.next = current.next;
        current.next.prev = previous;
      }
      length--;
      return true;
    } else {
      return false;
    }
  };

  //其他方法与单向链表相同
}