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

单链表

import { defaultEquals } from '../util';

class Node {
  constructor(key) {
    this.key = key;
    this.left = undefined;
    this.right = undefined;
  }
  
class LinkedList {
    constructor(equalsFn = defaultEquals) {
        this.equalsFn = equalsFn;
        this.count = 0;
        this.head = undefined;
      }
      
      //向链表尾部添加一个新元素
      push(element) {
           const node = new Node(element); //把 element 作为值传入,创建 Node 项。当一个 Node 实例被创建时,它的 next 指针总是 undefined
           let current;
          if (this.head == null) { // catches null && undefined链表为空,添加的是第一个元素
            this.head = node;
          } else { //链表不为空,向其追加元素
            current = this.head; //此时只有第一个元素的引用
            while (current.next != null) {
              current = current.next;
            } //首先找到最后一个元素,current.next == null表示达到尾部
            current.next = node; //然后要做的就是让当前(也就是最后一个)元素的 next 指针指 向想要添加到链表的节点
          }
          this.count++;
    }

    //返回链表中特定位置的元素。如果链表中不存在这样的元素, 则返回 undefined
    getElementAt(index) {
      if (index >= 0 && index <= this.count) {
        let node = this.head;
        for (let i = 0; i < index && node != null; i++) {
          node = node.next;
        }
        return node;
      }
      return undefined;
    }

    //向链表的特定位置插入一个新元素
    insert(element, index) {
      if (index >= 0 && index <= this.count) {
        const node = new Node(element);
        if (index === 0) {
          const current = this.head;
          node.next = current;
          this.head = node;
        } else {
          const previous = this.getElementAt(index - 1);
          node.next = previous.next;
          previous.next = node;
        }
        this.count++;
        return true;
      }
      return false;
    }

    //从链表的特定位置移除一个元素
    removeAt(index) {
      if (index >= 0 && index < this.count) { //检查越界
        let current = this.head;
        if (index === 0) { //移除第一个元素
          this.head = current.next;//让 head 指向列表的第二个元素
        } else { //移除第一个元素以外的元素
          const previous = this.getElementAt(index - 1);
          current = previous.next;
          previous.next = current.next;
        }
        this.count--;
        return current.element;
      }
      return undefined;
    }

    //从链表中移除一个元素
    remove(element) {
      const index = this.indexOf(element);
      return this.removeAt(index);
    }
    //因为我们有了 indexOf 方
法,如果传入元素的值,就可以找到它的位置,调用 removeAt 方法并传入该位置。而且如果我们要修改 removeAt 方法的代码的话会更简单——它会同时修改两个方法(这就是复 用代码的好处)。这样,我们不用维护两个用来移除链表元素的方法——只需要维护一个!

    //返回元素在链表中的索引。如果链表中没有该元素则返回-1
    indexOf(element) {
      let current = this.head;
      for (let i = 0; i < this.size() && current != null; i++) {
        if (this.equalsFn(element, current.element)) {
          return i;
        }
        current = current.next;
      }
      return -1;
    }
    //equalFn 函数的默认值如下。
//function defaultEquals(a, b) { //return a === b;
//}
    
    //如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false
    isEmpty() {
      return this.size() === 0;
    }

    //返回链表包含的元素个数,与数组的 length 属性类似
    size() {
      return this.count;
    }

    getHead() {
      return this.head;
    }

    clear() {
      this.head = undefined;
      this.count = 0;
    }

    //返回表示整个链表的字符串。由于列表项使用了 Node 类,就需要重写继 承自 JavaScript对象默认的 toString 方法,让其只输出元素的值
    toString() {
      if (this.head == null) {
        return '';
      }
      let objString = `${this.head.element}`;
      let current = this.head.next;
      for (let i = 1; i < this.size() && current != null; i++) {
        objString = `${objString},${current.element}`;
        current = current.next;
      }
      return objString;
    }
}

双向链表

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

import { defaultEquals } from '../util';

class DoublyNode extends Node {
    constructor(element, next, prev) {
        super(element, next);
        this.prev = prev; // 新增的
    }
}

class DoublyLinkedList extends LinkedList {
    constructor(equalsFn = defaultEquals) {
      super(equalsFn);
      this.tail = undefined; //新增的
    }

    push(element) {
      const node = new DoublyNode(element);
      if (this.head == null) {
        this.head = node;
        this.tail = node; // NEW
      } else {
        // attach to the tail node // NEW
        this.tail.next = node;
        node.prev = this.tail;
        this.tail = node;
      }
      this.count++;
    }

    insert(element, index) {
      if (index >= 0 && index <= this.count) {
        const node = new DoublyNode(element);
        let current = this.head;
        if (index === 0) {
          if (this.head == null) { // NEW
            this.head = node;
            this.tail = node; // NEW
          } else {
            node.next = this.head;
            this.head.prev = node; // NEW
            this.head = node;
          }
        } else if (index === this.count) { // last item NEW
          current = this.tail;
          current.next = node;
          node.prev = current;
          this.tail = node;
        } else {
          const previous = this.getElementAt(index - 1);
          current = previous.next;
          node.next = current;
          previous.next = node;
          current.prev = node; // NEW
          node.prev = previous; // NEW
        }
        this.count++;
        return true;
      }
      return false;
    }

    removeAt(index) {
      if (index >= 0 && index < this.count) {
        let current = this.head;
        if (index === 0) {
          this.head = this.head.next;
          // if there is only one item, then we update tail as well //NEW
          if (this.count === 1) {
            // {2}
            this.tail = undefined;
          } else {
            this.head.prev = undefined;
          }
        } else if (index === this.count - 1) {
          // last item //NEW
          current = this.tail;
          this.tail = current.prev;
          this.tail.next = undefined;
        } else {
          current = this.getElementAt(index);
          const previous = current.prev;
          // link previous with current's next - skip it to remove
          previous.next = current.next;
          current.next.prev = previous; // NEW
        }
        this.count--;
        return current.element;
      }
      return undefined;
    }

    indexOf(element) {
      let current = this.head;
      let index = 0;
      while (current != null) {
        if (this.equalsFn(element, current.element)) {
          return index;
        }
        index++;
        current = current.next;
      }
      return -1;
    }

    getHead() {
      return this.head;
    }

    getTail() {
      return this.tail;
    }

    clear() {
      super.clear();
      this.tail = undefined;
    }

    toString() {
      if (this.head == null) {
        return '';
      }
      let objString = `${this.head.element}`;
      let current = this.head.next;
      while (current != null) {
        objString = `${objString},${current.element}`;
        current = current.next;
      }
      return objString;
    }

    inverseToString() {
      if (this.tail == null) {
        return '';
      }
      let objString = `${this.tail.element}`;
      let previous = this.tail.prev;
      while (previous != null) {
        objString = `${objString},${previous.element}`;
        previous = previous.prev;
      }
      return objString;
    }
}

循环链表

class CircularLinkedList extends LinkedList {
  constructor(equalsFn = defaultEquals) {
    super(equalsFn);
  }

  push(element) {
    const node = new Node(element);
    let current;
    if (this.head == null) {
      this.head = node;
    } else {
      current = this.getElementAt(this.size() - 1);
      current.next = node;
    }
    // set node.next to head - to have circular list
    node.next = this.head;
    this.count++;
  }

  insert(element, index) {
    if (index >= 0 && index <= this.count) {
      const node = new Node(element);
      let current = this.head;
      if (index === 0) {
        if (this.head == null) {
          // if no node  in list
          this.head = node;
          node.next = this.head;
        } else {
          node.next = current;
          current = this.getElementAt(this.size());
          // update last element
          this.head = node;
          current.next = this.head;
        }
      } else {
        const previous = this.getElementAt(index - 1);
        node.next = previous.next;
        previous.next = node;
      }
      this.count++;
      return true;
    }
    return false;
  }

  removeAt(index) {
    if (index >= 0 && index < this.count) {
      let current = this.head;
      if (index === 0) {
        if (this.size() === 1) {
          this.head = undefined;
        } else {
          const removed = this.head;
          current = this.getElementAt(this.size() - 1);
          this.head = this.head.next;
          current.next = this.head;
          current = removed;
        }
      } else {
        // no need to update last element for circular list
        const previous = this.getElementAt(index - 1);
        current = previous.next;
        previous.next = current.next;
      }
      this.count--;
      return current.element;
    }
    return undefined;
  }
}

有序链表

class SortedLinkedList extends LinkedList {
  constructor(equalsFn = defaultEquals, compareFn = defaultCompare) {
    super(equalsFn);
    this.equalsFn = equalsFn;
    this.compareFn = compareFn;
  }

  push(element) {
    if (this.isEmpty()) {
      super.push(element);
    } else {
      const index = this.getIndexNextSortedElement(element);
      super.insert(element, index);
    }
  }

  insert(element, index = 0) {
    if (this.isEmpty()) {
      return super.insert(element, index === 0 ? index : 0);
    }
    const pos = this.getIndexNextSortedElement(element);
    return super.insert(element, pos);
  }

  getIndexNextSortedElement(element) {
    let current = this.head;
    let i = 0;
    for (; i < this.size() && current; i++) {
      const comp = this.compareFn(element, current.element);
      if (comp === Compare.LESS_THAN) {
        return i;
      }
      current = current.next;
    }
    return i;
  }
}

StackLinkedList 类

我们还可以使用 LinkedList 类及其变种作为内部的数据结构来创建其他数据结构,例如
栈、队列和双向队列

import DoublyLinkedList from './doubly-linked-list';

class StackLinkedList {
  constructor() {
    this.items = new DoublyLinkedList();
  }

  push(element) {
    this.items.push(element);
  }

  pop() {
    if (this.isEmpty()) {
      return undefined;
    }
    const result = this.items.removeAt(this.size() - 1);
    return result;
  }

  peek() {
    if (this.isEmpty()) {
      return undefined;
    }
    return this.items.getElementAt(this.size() - 1).element;
  }

  isEmpty() {
    return this.items.isEmpty();
  }

  size() {
    return this.items.size();
  }

  clear() {
    this.items.clear();
  }

  toString() {
    return this.items.toString();
  }
}
 posted on 2020-11-16 15:18  chen_coder  阅读(116)  评论(0)    收藏  举报