JavaScript数据结构与算法之链表

83 阅读2分钟

一、链表基础

  • 优点
    • 链表在内存中的存储空间不连续,可以灵活的利用存储空间
    • 相对于数组存储空间必须是连续的一段,链表更为灵活
    • 数组在头部增加/删除数据,效率最为低下,链表则不一样,时间复杂度为O(1)
      • 数组空间需要拓展时,一般为先申请一块更大的连续的存储空间,并将原数组的数据进行拷贝
      • 当在头部增加数据时,则原本的第一位及其后的数据都得往后挪动一位,效率比较低
  • 缺点
    • 链表无法像数组那样根据下标来快速定位到对应的元素,只能通过指针遍历从头一个一个查找
  • 总结
    • 当线性表的长度变化不大、易于确定其大小时,采用顺序表作为存储结构。
    • 若线性表主要操作是查找。很少进行插入或删除操作时,采用顺序表作为存储结构。
    • 对于频繁进行插入和删除的线性表,则应该使用链表作为存储结构。

二、实现代码

// 链表

function LinkedList() {
  // 长度
  this.length = 0;
  // 表头
  this.head = null;
  // 创建数据
  function Node(element) {
    this.element = element;
    this.next = null;
  }
  // 向列表尾部添加新的项
  LinkedList.prototype.append = (element) => {
    const node = new Node(element);
    // 判断添加的位置是不是第一个
    if (!this.head) {
      this.head = node;
    } else {
      // 循环找到最后一个节点
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = node;
    }
    this.length += 1;
  };
  // 特定位置插入新的项D
  LinkedList.prototype.insert = (position, element) => {
    if (position < 0 || position > this.length) return false;
    const node = new Node(element);
    if (position === 0) {
      node.next = this.head;
      this.head = node;
    } else {
      let current = this.head;
      let previous = null;
      let index = 0;
      while (index++ < position) {
        previous = current;
        current = current.next;
      }
      node.next = current;
      previous.next = node;
    }
    this.length += 1;
    return true;
  };
  // 获取对位置的项
  LinkedList.prototype.get = (position) => {
    if (position < 0 || position >= this.length) return null;
    let index = 0;
    let current = this.head;
    while (index++ < position) {
      current = current.next;
    }
    return current.element;
  };
  // 返回索引,若没有则返回 -1
  LinkedList.prototype.indexOf = (element) => {
    let current = this.head;
    let index = 0;
    while (current) {
      if (current.element === element) {
        return index;
      }
      current = current.next;
      index++;
    }
    return -1;
  };
  // 更新某个位置的数据
  LinkedList.prototype.update = (position, element) => {
    if (position < 0 || position >= this.length) return false;
    let index = 0;
    let current = this.head;
    while (index++ < position) {
      current = current.next;
    }
    current.element = element;
    return true;
  };
  // 删除特定位置
  LinkedList.prototype.removeAt = (position) => {
    if (position < 0 || position >= this.length) return false;
    let current = this.head;
    if (position == 0) {
      this.head = current.next;
    } else {
      let index = 0;
      let previous = null;
      while (index++ < position) {
        previous = current;
        current = current.next;
      }
      previous.next = current.next;
    }
    this.length -= 1;
  };
  // 删除指定项
  LinkedList.prototype.remove = (element) => {
    const position = this.indexOf(element);
    return this.removeAt(position);
  };
  // 判断是否为空
  LinkedList.prototype.isEmpty = () => {
    return this.length === 0;
  };
  // 获取长度
  LinkedList.prototype.size = () => {
    return this.length;
  };
  LinkedList.prototype.toString = () => {
    let resultString = "";
    let current = this.head;
    while (current) {
      resultString += current.element + " ";
      current = current.next;
    }
    return resultString;
  };
}

const linkedList = new LinkedList();

linkedList.append("【1】我是第一个");
linkedList.append("【2】我是第二个");
linkedList.append("【3】我是第三个");
linkedList.insert(0, "【4】我是路过的");
linkedList.insert(2, "【5】我是插队的");
console.log("linkedList.size()", linkedList.size());

linkedList.insert(5, "【6】我是来排队的");
linkedList.update(5, "我更新咯");

linkedList.removeAt(5);
console.log("linkedList.toString()", linkedList.toString());
linkedList.remove("【3】我是第三个");
console.log("linkedList.toString()", linkedList.toString());