首页 > 技术文章 > 链表

Archer-Fang 2019-01-06 17:21 原文


 

复杂度


时间复杂度


获取:O(n)


查询:O(n)


插入:O(1)


删除:O(1)


空间复杂度


O(n)


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

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

note:

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

这段代码的作用如下:

 const nodeValue = { value: 1, key: 'test' };
    const node = new LinkedListNode(nodeValue);
    const toStringCallback = value => `value: ${value.value}, key: ${value.key}`;

    expect(node.toString(toStringCallback)).toBe('value: 1, key: test');

如果写成:

expect(node.toString()).toBe('value: 1, key: test');

测试会报错

Expected: "value: 1, key: test"

Received: "[object Object]"

所以,

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

这段代码的功能为解析对象成为二个字段value和key

 

LinkList:如果链表不为空,则头尾指针不为空的代码。

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

export default class LinkedList {//链表类
  /**
   * @param {Function} [comparatorFunction]
   */
  constructor(comparatorFunction) {//构造函数
    /** @var LinkedListNode */
    this.head = null;//头节点指针

    /** @var LinkedListNode */
    this.tail = null;//尾节点指针

    this.compare = new Comparator(comparatorFunction);//新实例化一个比较器对象存成compare属性
  }

  /**
   * @param {*} value
   * @return {LinkedList}
   */
  prepend(value) {//在链表最前面添加一个新的节点作为头节点,头插法
    // Make new node to be a head.
    const newNode = new LinkedListNode(value, this.head);//添加新节点在头节点之前
    this.head = newNode;//头指针指向新增节点

    // If there is no tail yet let's make new node a tail.
    if (!this.tail) {//如果没有尾节点,尾指针也指向新节点
      this.tail = newNode;
    }

    return this;//返回链表对象
  }

  /**
   * @param {*} value
   * @return {LinkedList}
   */
  append(value) {//在链表最后面添加一个新节点作为尾节点,尾插法
    const newNode = new LinkedListNode(value);//根据value创建一个新节点

    // If there is no head yet let's make new node a head.
    if (!this.head) {//如果还没有头节点,头指针和尾指针都指向当前新节点
      this.head = newNode;
      this.tail = newNode;

      return this;
    }

    // Attach new node to the end of linked list.
    this.tail.next = newNode;//将旧的尾节点和新尾节点连接起来
    this.tail = newNode;//尾指针指向新节点

    return this;
  }

  /**
   * @param {*} value
   * @return {LinkedListNode}
   */
  delete(value) {//删除值对应的节点
    if (!this.head) {//如果链表没有节点,返回null
      return null;
    }

    let deletedNode = null;//要删除的节点

    // If the head must be deleted then make next node that is differ
    // from the head to be a new head.
    //如果头节点必须被删除,就要确保下一个节点和头节点是不一样的新节点
    while (this.head && this.compare.equal(this.head.value, value)) {
      //循环条件:头指针存在且头节点的值和要删除的值相等
      deletedNode = this.head;//要被删除的头节点赋值给deletedNode
      this.head = this.head.next;//头指针指向头节点的下一个节点
    }

    let currentNode = this.head;//当前头节点

    if (currentNode !== null) {
      // If next node must be deleted then make next node to be a next next one.
      //如果下一个节点必须被删除,就让下一个节点变成下下个节点
      while (currentNode.next) {
        if (this.compare.equal(currentNode.next.value, value)) {
          deletedNode = currentNode.next;//要被删除的几点赋值给deletedNode
          currentNode.next = currentNode.next.next;//下下个节点赋值给下个节点
        } else {//如果下个节点不删除,就继续往下遍历
          currentNode = currentNode.next;
        }
      }
    }

    // Check if tail must be deleted.
    //如果尾节点要被删除就重新赋值尾指针
    if (this.compare.equal(this.tail.value, value)) {
      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) {//如果链表没有节点,返回null
      return null;
    }

    let currentNode = this.head;//currentNode存头节点

    while (currentNode) {
      // If callback is specified then try to find node by callback.
      //如果指定了回调函数,就用回调函数来查找节点
      if (callback && callback(currentNode.value)) {
        return currentNode;
      }

      // If value is specified then try to compare by value..
      //如果指定了value就比较value是否相等来查找节点
      if (value !== undefined && this.compare.equal(currentNode.value, value)) {
        return currentNode;
      }

      currentNode = currentNode.next;//继续循环比较下一个节点
    }

    return null;//找不到返回null
  }

  /**
   * @return {LinkedListNode}
   */
  deleteTail() {//删除尾节点
    const deletedTail = this.tail;//存下尾节点

    if (this.head === this.tail) {//如果链表只有一个节点,头尾指针都置空然后返回被删除的尾节点
      // There is only one node in linked list.
      this.head = null;
      this.tail = null;

      return deletedTail;
    }

    // If there are many nodes in linked list...

    // Rewind to the last node and delete "next" link for the node before the last one.
    //如果链表有许多节点,就从头开始遍历,找到尾节点的前一个节点,将它的next指针置空即可
    let currentNode = this.head;
    while (currentNode.next) {
      if (!currentNode.next.next) {
        currentNode.next = null;
      } else {
        currentNode = currentNode.next;
      }
    }

    this.tail = currentNode;//设置尾指针

    return deletedTail;//返回被删除的尾节点
  }

  /**
   * @return {LinkedListNode}
   */
  //删除头节点
  deleteHead() {
    if (!this.head) {//如果链表没有节点,返回null
      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) {//遍历链表,将节点的值push到结果数组
      nodes.push(currentNode);
      currentNode = currentNode.next;
    }

    return nodes;
  }

  /**
   * @param {function} [callback]
   * @return {string}
   */
  toString(callback) {//返回将链表所有节点的值调用节点自身的toString方法经由callback处理后的值变成字符串组成的数组
    return this.toArray().map(node => node.toString(callback)).toString();
  }

  /**
   * Reverse a linked list.
   * @returns {LinkedList}
   */
  //将链表顺序反转
  reverse() {
    let currNode = this.head;
    let prevNode = null;
    let nextNode = null;

    while (currNode) {
      // Store next node.
      nextNode = currNode.next;//存下下一个节点

      // Change next node of the current node so it would link to previous node.
      currNode.next = prevNode;//当前节点的下一个节点赋值为上一个节点

      // Move prevNode and currNode nodes one step forward.
      prevNode = currNode;//上一个节点赋值为当前节点
      currNode = nextNode;//当前节点赋值为下一个节点
    }

    // Reset head and tail.
    //反转顺序处理完后重置头尾指针
    this.tail = this.head;
    this.head = prevNode;

    return this;
  }
}

 

 

Comparator:

export default class Comparator {//比较器类,用于实例化一个对象,上面有各种用于比较的工具方法
  /**
   * @param {function(a: *, b: *)} [compareFunction]
   */
  constructor(compareFunction) {//构造函数可以传入自定义的比较方法
    this.compare = compareFunction || Comparator.defaultCompareFunction;
    //对象上的compare属性是自定义比较方法或者默认的比较方法
  }

  /**
   * @param {(string|number)} a
   * @param {(string|number)} b
   * @returns {number}
   */
  static defaultCompareFunction(a, b) {//默认比较方法,相等返回0,小于返回-1,大于返回1
    if (a === b) {
      return 0;
    }

    return a < b ? -1 : 1;
  }

  equal(a, b) {//判断两个值是否相等
    return this.compare(a, b) === 0;
  }

  lessThan(a, b) {//判断是否a<b
    return this.compare(a, b) < 0;
  }

  greaterThan(a, b) {//判断是否a>b
    return this.compare(a, b) > 0;
  }

  lessThanOrEqual(a, b) {//判断a<=b
    return this.lessThan(a, b) || this.equal(a, b);
  }

  greaterThanOrEqual(a, b) {//判断a>=b
    return this.greaterThan(a, b) || this.equal(a, b);
  }

  reverse() {//a和b调换位置比较
    const compareOriginal = this.compare;
    this.compare = (a, b) => compareOriginal(b, a);
  }
}

 

const nodeValue = { value: 1, key: 'test' };
const node = new LinkedListNode(nodeValue);
const toStringCallback = value => `value: ${value.value}, key: ${value.key}`;

expect(node.toString(toStringCallback)).toBe('value: 1, key: test');

推荐阅读