JS手抄数据结构之链表

193 阅读2分钟

LinkedList.js

import LinkedListNode from './LinkedListNode';
import Comparator from '../../utils/comparator/Comparator';

export default class LinkedList {
  /**
   * @param {Function} [comparatorFunction]
   */
  constructor(comparatorFunction) {
    /** @var LinkedListNode */
    this.head = null;     //初始化this.head指针用于维护链表头节点

    /** @var LinkedListNode */
    this.tail = null;     //初始化this.head指针用于维护链表尾节点

    this.compare = new Comparator(comparatorFunction);     //导入自定义的比较函数
  }

  /**
   * @param {*} value
   * @return {LinkedList}
   */
  prepend(value) {       //  用于在链表头之前插入数据
    // Make new node to be a head.
    //创建一个新链表节点的同时,传入this.head作为其next指针(新插入的节点将指向原先的头节点),如果链表原来为空,则传入null
    const newNode = new LinkedListNode(value, this.head);
    this.head = newNode;    //将this.head置于当前节点

    // If there is no tail yet let's make new node a tail.
    if (!this.tail) {
      this.tail = newNode;  //  如果this.tail指针没有指向,则将其指向tail,(说明原来是个空链表)
    }

    return this;
  }

  /**
   * @param {*} value
   * @return {LinkedList}
   */
  append(value) {   //  用于在链表尾部
    const newNode = new LinkedListNode(value);  //创建一个新的链表节点,单向链表不用考虑前面的指针,next指针默认为null,只需传入value

    //如果是空链表,则将this.head/this.tail指针都指向该新节点
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;

      return this;   //函数执行的结果返回实例对象。。都是这样。。在这里面
    }

    //链表不为空,则将链表末尾节点的next指向新节点,this.tail指针指向该节点,this.head指针不用动
    this.tail.next = newNode;
    this.tail = newNode;

    return this;
  }

  /**
   * @param {*} value
   * @param {number} index
   * @return {LinkedList}
   */
  insert(value, rawIndex) {
    const index = rawIndex < 0 ? 0 : rawIndex;    //如果索引为负,置0,默认从最开始最前面插入
    if (index === 0) {
      this.prepend(value);
    } else {
      let count = 1;
      let currentNode = this.head;     //指针从表头开始遍历
      const newNode = new LinkedListNode(value);
      while (currentNode) {
        if (count === index) break;    //移动指针到对应索引位置的节点
        currentNode = currentNode.next;
        count += 1;
      }
      if (currentNode) {     //索引没有超出链表索引,所以在指定节点后插入新的链表节点
        newNode.next = currentNode.next;
        currentNode.next = newNode;
      } else {
        if (this.tail) {    //当前索引超出了链表索引
          this.tail.next = newNode;
          this.tail = newNode;
        } else {      //这种情况是空链表
          this.head = newNode;
          this.tail = newNode;
        }
      }
    }
    return this;
  }

  /**
   * @param {*} value
   * @return {LinkedListNode}
   */
  delete(value) {     //首先明确一点,delete(value)是会删除链表中的所有value,然后可以开始了
    if (!this.head) {  //说明是空数组,返回null
      return null;
    }

    let deletedNode = null;   //初始化delete,默认要删除的值不存在

    // 如果必须删除头,则将与头不同的下一个节点作为一个新的头。
    while (this.head && this.compare.equal(this.head.value, value)) {
      deletedNode = this.head;    //这是为了返回你删除的节点的obj
      this.head = this.head.next;   //如果要删除头,将this.head指向下一个node节点,头结点就被排除在链表外了
    }

    //不用删除头,走到这一步,说明不用删除头节点  or 头结点被while循环给干完了,现在的头节点不用删
    let currentNode = this.head;   //currentNode为当下this.head所指节点

    if (currentNode !== null) {    //节点存在,
      // 如果必须删除下一个节点,则使下一个节点成为下一个节点。
      while (currentNode.next) {     //因为当下头结点不用删,直接开始currentNode.next的节点值的比较
        if (this.compare.equal(currentNode.next.value, value)) {
          deletedNode = currentNode.next;    //这是为了返回你删除的节点的obj
          currentNode.next = currentNode.next.next;   //删除下一个节点
        } else {
          currentNode = currentNode.next;    //将当前节点移动到下一个节点
        }
      }
    }

    // Check if tail must be deleted.
    if (this.compare.equal(this.tail.value, value)) {  //我不知道这一步有什么意义,不要这一步好像也可以。。。pass了
      this.tail = currentNode;
    }

    return deletedNode;
  }

  /**
   * @param {Object} findParams
   * @param {*} findParams.value
   * @param {function} [findParams.callback]
   * @return {LinkedListNode}
   */
  find({ value = undefined, callback = undefined }) {  //这里传入的是一个配置对象
    if (!this.head) { //说明是空链表
      return null;
    }

    let currentNode = this.head;

    while (currentNode) {
      // 如果指定了回调函数,就通过回调函数来查找值
      if (callback && callback(currentNode.value)) {   //fn传入value,根据返回的值判断是否查找到
        return currentNode;
      }

      // 没有回调函数就通过值的比较来查找值
      if (value !== undefined && this.compare.equal(currentNode.value, value)) {
        return currentNode;
      }

      currentNode = currentNode.next;
    }

    return null;
  }

  /**
   * @return {LinkedListNode}
   */
  deleteTail() {
    const deletedTail = this.tail;    //默认删除的未尾节点,并返回

    if (this.head === this.tail) {
      // 链表里只有一个节点
      this.head = null;
      this.tail = null;

      return deletedTail;
    }

    // 如果链表中有很多节点…

    // 回退到最后一个节点,删除最后一个节点之前的“下一个”链接。
    let currentNode = this.head;
    while (currentNode.next) {
      if (!currentNode.next.next) {  //如果没有下下个节点,下个节点就是尾节点
        currentNode.next = null;   //将当前节点的next节点置为null
      } else {
        currentNode = currentNode.next;   //不然就将当前节点往后移
      }
    }

    this.tail = currentNode;   //没有节点就是返回null了

    return deletedTail;
  }

  /**
   * @return {LinkedListNode}
   */
  deleteHead() {
    if (!this.head) {
      return null;
    }

    const deletedHead = this.head;

    if (this.head.next) {   //...很前面差不多
      this.head = this.head.next;
    } else {
      this.head = null;
      this.tail = null;
    }

    return deletedHead;
  }

  /**
   * @param {*[]} values - Array of values that need to be converted to linked list.
   * @return {LinkedList}
   */
  fromArray(values) {
    values.forEach((value) => this.append(value));  //输入数组,转成链表

    return this;
  }

  /**
   * @return {LinkedListNode[]}
   */
  toArray() {
    const nodes = [];

    let currentNode = this.head;
    while (currentNode) {
      nodes.push(currentNode);
      currentNode = currentNode.next;
    }

    return nodes;
  }

  /**
   * @param {function} [callback]
   * @return {string}
   */
  toString(callback) {
    return this.toArray().map((node) => node.toString(callback)).toString();
  }

  /**
   * 反转链表
   * @returns {LinkedList}
   */
  reverse() {
    let currNode = this.head;
    let prevNode = null;
    let nextNode = null;

    while (currNode) {
      // 不停的到下一个节点,将下一个节点指向当前节点
      nextNode = currNode.next;

      //第一次就是指向了null,后续就都是指向前一个节点
      currNode.next = prevNode;

      // 将prevNode和currNode节点向前移动一步,这个reverse画个图就很清晰了
      prevNode = currNode;
      currNode = nextNode;   //当currNode是null时,说明已经完成了尾节点的指针指向,因为,pervNode指向了null


    }

    // Reset head and tail.
    this.tail = this.head;
    this.head = prevNode;

    return this;
  }
}

LinkedListNode.js

export default class LinkedListNode {
  constructor(value, next = null) {
    this.value = value;
    this.next = next;
  }

  toString(callback) {
    return callback ? callback(this.value) : `${this.value}`;
  }
}

Comparator.js

export default class Comparator {
  /**
   * Constructor.
   * @param {function(a: *, b: *)} [compareFunction] - It may be custom compare function that, let's
   * say may compare custom objects together.
   * 它可能是一个自定义的比较函数,比如说,可以把自定义的对象比较在一起
   */
  constructor(compareFunction) {
    this.compare = compareFunction || Comparator.defaultCompareFunction;
  }

  /**
   * Default comparison function. It just assumes that "a" and "b" are strings or numbers.
   * @param {(string|number)} a
   * @param {(string|number)} b
   * @returns {number}
   */
  static defaultCompareFunction(a, b) {
    if (a === b) {
      return 0;
    }

    return a < b ? -1 : 1;
  }

  /**
   * Checks if two variables are equal.
   * @param {*} a
   * @param {*} b
   * @return {boolean}
   */
  equal(a, b) {
    return this.compare(a, b) === 0;
  }

  /**
   * Checks if variable "a" is less than "b".
   * @param {*} a
   * @param {*} b
   * @return {boolean}
   */
  lessThan(a, b) {
    return this.compare(a, b) < 0;
  }

  /**
   * Checks if variable "a" is greater than "b".
   * @param {*} a
   * @param {*} b
   * @return {boolean}
   */
  greaterThan(a, b) {
    return this.compare(a, b) > 0;
  }

  /**
   * Checks if variable "a" is less than or equal to "b".
   * @param {*} a
   * @param {*} b
   * @return {boolean}
   */
  lessThanOrEqual(a, b) {
    return this.lessThan(a, b) || this.equal(a, b);
  }

  /**
   * Checks if variable "a" is greater than or equal to "b".
   * @param {*} a
   * @param {*} b
   * @return {boolean}
   */
  greaterThanOrEqual(a, b) {
    return this.greaterThan(a, b) || this.equal(a, b);
  }

  /**
   * Reverses the comparison order.
   */
  reverse() {
    const compareOriginal = this.compare;
    this.compare = (a, b) => compareOriginal(b, a);
  }
}