JavaScript:实现链表

70 阅读2分钟

一、优缺点

相对于数组链表有一些优点

  • 内存空间不是必须连续的.可以充分利用计算机的内存,实现灵活的内存动态管理.
  • 链表不必在创建时就确定大小,并且大小可以无限的延伸下去.
  • 链表在插入和删除数据时,时间复杂度可以达到0(1).相对数组效率高很多.

相对于数组,链表有一-些缺点

  • 链表访问任何一个位置的元素时,都需要从头开始访问.(无法跳过第一个元素访问任何一个元素).
  • 无法通过下标直接访问元素,需要从头一个个访问,直到找到对应的元素.

二、单向链表

image.png

// 构造函数
function LinkedList() {

  function Node(data) {
    this.data = data
    this.next = null
  }

  this.head = null
  this.length = 0;

  LinkedList.prototype.append = function(element) {
    var node = new Node(element)

    if (this.length === 0) {
      this.head = node
    }
    else {
      var current = this.head
      while (current.next) {
        current = current.next
      }
      current.next = node
    }

    this.length += 1

    return element
  }

  LinkedList.prototype.insert = function(position, element) {
    // 1、对position进行越界判断
    // 注:position不能等于this.length,否则无法append最后一位
    if (position < 0 || position > this.length) return false;

    // 2、根据data创建newNode
    var newNode = new Node(element)

    // 3、判断插入的位置是否为第一个
    if (position === 0) {
      newNode.next = this.head
      this.head = newNode
    }
    else {
      var current = this.head;
      var previous = null
      for (var i=0; i<position; i++) {
        previous = current
        current = current.next
      }

      newNode.next = current
      previous.next = newNode
    }

    this.length += 1

    return true
  }

  LinkedList.prototype.get = function(position) {

    // 1、对position进行越界判断
    if (position < 0 || position >= this.length) return;

    var current = this.head;
    for (var i=0; i<position; i++) {
      current = current.next
    }
    return current.data
  }

  LinkedList.prototype.indexOf = function(data) {
    var current = this.head;
    if (!current) {
      return -1;
    }

    var index = 0;
    while (current) {
      if (current.data === data) {
        return index
      }
      else {
        current = current.next
        index += 1
      }
    }
    return -1;
  }

  LinkedList.prototype.update = function(position, data) {
    // 越界判断
    if (position < 0 || position >= this.length) return false;

    let current = this.head
    for (let i=0; i<position; i++) {
      current = current.next
    }
    current.data = data
    return true
  }

  LinkedList.prototype.removeAt = function(position) {
    // 越界判断
    if (position < 0 || position >= this.length) return false

    let current = this.head;
    if (position === 0) {
      this.head = this.head.next;
    }
    else {
      let previous = null;
      for (let i=0; i<position; i++) {
        previous = current
        current = current.next
      }

      previous.next = current.next
    }

    this.length -= 1

    return current.data
  }

  LinkedList.prototype.remove = function(data) {

    // 方案1:手写
    /*
    let current = this.head
    if (this.head.data === data) {
      this.head = this.head.next
    }
    else {
      let exsisted = false;
      let previous = null
      while (current) {
        if (current.data === data) {
          previous.next = current.next
          exsisted = true
          break;
        }
        else {
          previous = current
          current = current.next
        }
      }

      if (!exsisted) {
        return false
      }

      this.length -= 1
    }

    return current.data;
    */

    // 方案2:借用removeAt
    const position = this.indexOf(data)

    return this.removeAt(position)
  }

  // 2、获取对应位置对数据
  LinkedList.prototype.toString = function() {
    var listString = ''
    var current = this.head

    while (current) {
      listString += current.data + ' '
      current = current.next
    }

    return listString
  }

  LinkedList.prototype.isEmpty = function() {
    return this.length === 0
  }

  LinkedList.prototype.size = function() {
    return this.length
  }

}

三、双向链表

image.png

function DoublyLinkedList() {

  function Node(data) {
    this.data = data
    this.prev = null
    this.next = null
  }

  this.head = null
  this.tail = null
  this.length = 0

  DoublyLinkedList.prototype.append = function(data) {
    const newNode = new Node(data)
    if (this.length === 0) {
      this.head = newNode
      this.tail = newNode
    }
    else{
      this.tail.next = newNode
      newNode.prev = this.tail
      this.tail = newNode
    }

    this.length += 1;

    return true
  }

  DoublyLinkedList.prototype.insert = function(position, data) {
    // 越界判断
    if (position < 0 || position > this.length) return false

    const newNode = new Node(data)

    if (this.length === 0) {
      this.head = newNode
      this.tail = newNode
    }
    else {
      if (position === 0) {
        newNode.next = this.head
        this.head.prev = newNode
        this.head = newNode
      }
      else if (position === this.length) {
        this.tail.next = newNode
        newNode.prev = this.tail
        this.tail = newNode
      }
      else {
        let current = this.head
        for (let i=0; i<position; i++) {
          current = current.next
        }

        newNode.next = current
        newNode.prev = current.prev
        current.prev.next = newNode
        current.prev = newNode
      }
    }

    this.length += 1

    return true
  }

  DoublyLinkedList.prototype.get = function(position) {
    // 越界判断
    if (position < 0 || position >= this.length) return false

    if (position < (this.length / 2)) { // 从头向尾遍历
      let current = this.head
      for (let i=0; i<position; i++) {
        console.log('111')
        current = current.next
      }
      return current.data
    }
    else { // 从尾向头遍历
      let current = this.tail
      for (let i=this.length-1; i>position ; i--) {
        console.log('222')
        current = current.prev
      }
      return current.data
    }
  }

  DoublyLinkedList.prototype.indexOf = function(data) {
    let current = this.head;

    let index = 0;
    while (current) {
      if (current.data === data) {
        return index
      }
      current = current.next
      index += 1;
    }
    return -1
  }

  DoublyLinkedList.prototype.update = function(position, data) {
    // 越界判断
    if (position < 0 || position >= this.length) return false

    let current = this.head
    for (let i=0; i < position; i++){
      current = current.next
    }

    current.data = data

    return true
  }

  DoublyLinkedList.prototype.removeAt = function(position) {
    // 越界判断
    if (position < 0 || position >= this.length) return null

    let current = this.head

    if (this.length === 1) {
      this.head = null
      this.tail = null
    }
    else {
      if (position === 0) {
        this.head.next.prev = null
        this.head = this.head.next
      }
      else if (position === this.length-1) {
        current = this.tail

        this.tail.prev.next = null
        this.tail = this.tail.prev
      }
      else {
        for (let i=0; i<position; i++) {
          current = current.next
        }
        current.prev.next = current.next
        current.next.prev = current.prev
      }
    }

    this.length -= 1

    return current.data
  }

  DoublyLinkedList.prototype.remove = function(data) {

    // 借用indexOf和removeAt
    const index = this.indexOf(data)

    return this.removeAt(index)

    // 原始方法
    /*
      if (this.length === 0) {
        return false
      }
      else if (this.length === 1 && this.head.data === data) {
        this.head = null
        this.tail = null
        return true
      }
      else if (this.head.data === data) {
        this.head = this.head.next
        return true
      }
      else if (this.tail.data === data) {
        this.tail.prev.next = null
        this.tail = this.tail.prev
        return true
      }
      else {
        let current = this.head
        while (current) {
          if (current.data === data) {
            current.prev.next = current.next
            current.next.prev = current.prev
            return true
          }
          current = current.next
        }
      }
      return false
    */

  }

  DoublyLinkedList.prototype.toString = function() {
    return this.backwordString()
  }

  DoublyLinkedList.prototype.backwordString = function() {
    let current = this.head
    let resultString = ''

    while (current) {
      resultString += current.data + ' '
      current = current.next
    }

    return resultString
  }

  DoublyLinkedList.prototype.forwardString = function() {
    let current = this.tail
    let resultString = ''

    while (current) {
      resultString += current.data + ' '
      current = current.prev
    }

    return resultString
  }

  DoublyLinkedList.prototype.isEmpty = function() {
    return this.length === 0
  }

  DoublyLinkedList.prototype.size = function() {
    return this.length
  }

  DoublyLinkedList.prototype.getHead = function() {
    return this.head.data
  }

  DoublyLinkedList.prototype.getTail = function() {
    return this.tail.data
  }
}