JavaScript data structures [series] 05- LinkedList list

JavaScript data structures [series] 05- LinkedList list

Road workers CoderMonkey code
reproduced, please indicate the author and source



1. The chain structure recognized (singly linked list)

Also linear chain structure,

  • Linked list node configuration
  • Each node contains a pointer field and a data storage field
  • The relationship between the node pointer by domain representation

Reference list structural diagram of texture append method below

Compared to arrays, linked lists:

  • Not need to specify the initial size
  • No need to shrink capacity expansion
  • High memory usage
  • Ease of insertion and deletion of elements

--

  • Not directly accessible by the subscript, one by one required probe

2. The list of common methods

We will implement the following common methods:

method description
append(data) Add elements to the list
insert(position, data) To insert elements into the specified position
remove(data) Removing elements
removeAt(position) Delete the specified location element
update(position, data) Updates the specified location element
getItem(position) Find the specified location element
indexOf(data) Gets the element position
size() Gets the list size
isEmpty() Determine whether the list is empty
clear() Empty list
toString() Stringified

Note: At this time we will not consider the complexity of reference types

3. code implementation 

Note:
ES6 version code implements check out npm package data-struct-js code is
on Github / Gitee can be found

npm install data-struct-js

Packaging objectlist

/**
 * 链表:单向链表
 */
function LinkedList() {
  // 记录链表首个元素
  this.__head = null
  // 记录链表元素个数
  this.__count = 0

  // 用Node表示链表内部元素
  function Node(data) {
    this.data = data
    this.next = null

    Node.prototype.toString = function () {
      return this.data.toString()
    }
  }
}

3.1 append(data)

Achieve Analysis:

  1. When inserted into an empty list:

1.1 HEAD points to the new node inserted
1.2 Next the new node to point Null

LinkedList.append1.png

  1. When inserted into a non-empty list:

Next 2.1 at the end of the list of elements point to the new element
2.2 points to Null new elements Next

LinkedList.append2.png

LinkedList.prototype.append = function (data) {
  // 1.创建新元素
  var newNode = new Node(data)

  // 2.1链表为空时,直接添加到末尾
  if (this.__count === 0) {
    this.__head = newNode
  }
  // 2.2链表非空时,探查到末尾元素并添加新元素
  else {
    var current = this.__head
    while (current.next) {
      current = current.next
    }
    current.next = newNode
  }

  // 3.内部计数加1
  this.__count += 1

  return true
}

注:
添加元素方法,记得最后给元素个数记录加1
通过上图示例,体会:HEAD 概念和元素节点的 Next 指向修改

3.2 insert(position, data)

实现分析:

  • 插入方法接收两个参数:位置,数据
  • 可插入位置的范围:0~length
  • 插入目标位置:0 的情况
    • 新元素的 next 指向原首位元素
    • 将HEAD指向新元素
  • 循环到指定位置
    • 期间记录上一个元素及当前元素
    • 在上一个元素与当前元素中间加入要插入的元素
    • (修改相关指向,具体参考下面代码)
  • 插入元素方法,记得最后给元素个数记录加1
LinkedList.prototype.insert = function (position, data) {
  // 1.边界检查(插入位置)
  if (position < 0 || position > this.__count) return false

  // 2.创建新元素
  var newNode = new Node(data)

  // 3.1插入到链表头部
  if (position === 0) {
    newNode.next = this.__head
    this.__head = newNode
  }
  // 3.2以外(包括插入到末尾)
  else {
    var previous = null
    var current = this.__head
    var index = 0
    while (index < position) {
      previous = current
      current = current.next
      index++
    }
    previous.next = newNode
    newNode.next = current
  }
  // 4.内部计数加1
  this.__count += 1

  return true
}

注:只有在 insert 时的 position 检查规则与其它不同

3.3 remove(data)

实现分析:

  • 删除元素方法接收一个参数:数据
  • 根据指针循环查找
  • 将从参数收到的数据与当前元素的数据进行比较
    #复杂引用类型的时候通过传入自定义比较的回调函数来解决
  • 找到指定元素后,修改上一元素的 next 指向

注意当删除第一个元素时的特殊情况(修改HEAD指向)

删除元素完成后,记得最后给元素个数记录减1

LinkedList.prototype.remove = function (data) {
  var current = this.__head
  var previous = null

  while (current) {
    // 找到指定数据的元素,让当前元素不再被引用
    if (current.data == data) {
      if (previous == null) {
        // 没有前元素,要删除的是首元素,修改 Head 指针
        this.__head = current.next
      } else {
        // 修改前元素内部指针
        previous.next = current.next
      }
      // 内部计数减1
      this.__count -= 1
      // 处理完成,返回 true
      return true
    }
    previous = current
    current = current.next
  }
  // 查找到最后没有找到指定数据的元素,返回 false
  return false

  // 注:
  // 也可以通过调用 indexOf 获取下标后再调用 removeAt 来实现
  // 只是返回值会不同,看实际需要
}

3.4 removeAt(position)

实现分析:

  • 删除指定位置元素,接收一个参数:位置下标值
  • 基于元素指向循环查找
  • 到达指定下标元素时,将其前后元素关联,即达到删除效果

删除元素完成后,记得最后给元素个数记录减1

LinkedList.prototype.removeAt = function (position) {
  // 1.边界检查
  if (position < 0 || position >= this.__count) return false

  var index = 0
  var previous = null
  var current = this.__head

  // 2.找到指定位置元素
  while (index++ < position) {
    previous = current
    current = current.next
  }
  // 3.使当前元素不再被引用
  if (previous == null) {
    // position=0 删除首元素的时候
    this.__head = current.next
  } else {
    previous.next = current.next
  }

  // 4.内部计数减1
  this.__count -= 1

  return current.data
}

3.5 update(position, data)

实现分析:参看注释

LinkedList.prototype.update = function (position, data) {
  // 1.边界检查
  if (position < 0 || position >= this.__count) return false

  var current = this.__head
  var index = 0

  // 2.找到指定位置元素
  while (index++ < position) {
    current = current.next
  }
  // 3.修改当前元素数据
  current.data = data

  // 4.修改完成,返回 true
  return true
}

3.6 getItem(position)

获取指定位置元素的值

LinkedList.prototype.getItem = function (position) {
  // 边界检查
  if (position < 0 || position >= this.__count) return

  var index = 0
  var current = this.__head

  while (index < position) {
    current = current.next
    index += 1
  }
  return current.data
}

3.7 indexOf(data)

实现分析:

  • 获取元素所在位置下标值方法,接收一个参数:元素的数据
  • 根据元素 next 指向循环查找
  • 找到时返回当前下标
  • 找不到时返回 -1
LinkedList.prototype.indexOf = function (data) {
  var current = this.__head
  var index = 0

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

3.8 size()

查看元素个数方法

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

3.9 isEmpty()

判空方法

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

3.10 clear()

实现分析:
Head指向置空
计数清零

LinkedList.prototype.clear = function () {
  this.__head = null
  this.__count = 0
}

3.11 toString()

为了方便查看实现的字符串化方法

LinkedList.prototype.toString = function () {
  var str = '[HEAD] -> '
  var current = this.__head
  while (current) {
    str += current.data + ' -> '
    current = current.next
  }
  if (str === '[HEAD] -> ') {
    str = '[HEAD] -> Null'
  }
  return str
}

总结两点:

  • With the position index value related operations,
    it is to find the index value through the cycle,

Unlike the list structure array itself without subscripts.

  • All methods received index value
    should be carried out to check the boundary, wherein when the insert may be equal length

3.12 complete code

/**
 * 链表:单向链表
 */
function LinkedList() {
  // 记录链表首个元素
  this.__head = null
  // 记录链表元素个数
  this.__count = 0

  // 用Node表示链表内部元素
  function Node(data) {
    this.data = data
    this.next = null

    Node.prototype.toString = function () {
      return this.data.toString()
    }
  }

  /**
   * 添加节点
   */
  LinkedList.prototype.append = function (data) {
    // 1.创建新元素
    var newNode = new Node(data)

    // 2.1链表为空时,直接添加到末尾
    if (this.__count === 0) {
      this.__head = newNode
    }
    // 2.2链表非空时,探查到末尾元素并添加新元素
    else {
      var current = this.__head
      while (current.next) {
        current = current.next
      }
      current.next = newNode
    }

    // 3.内部计数加1
    this.__count += 1

    return true
  }

  /**
   * 插入节点
   */
  LinkedList.prototype.insert = function (position, data) {
    // 1.边界检查(插入位置)
    if (position < 0 || position > this.__count) return false

    // 2.创建新元素
    var newNode = new Node(data)

    // 3.1插入到链表头部
    if (position === 0) {
      newNode.next = this.__head
      this.__head = newNode
    }
    // 3.2以外(包括插入到末尾)
    else {
      var previous = null
      var current = this.__head
      var index = 0
      while (index < position) {
        previous = current
        current = current.next
        index++
      }
      previous.next = newNode
      newNode.next = current
    }
    // 4.内部计数加1
    this.__count += 1

    return true
  }

  /**
   * 删除节点
   */
  LinkedList.prototype.remove = function (data) {
    var current = this.__head
    var previous = null

    while (current) {
      // 找到指定数据的元素,让当前元素不再被引用
      if (current.data == data) {
        if (previous == null) {
          // 没有前元素,要删除的是首元素,修改 Head 指针
          this.__head = current.next
        } else {
          // 修改前元素内部指针
          previous.next = current.next
        }
        // 内部计数减1
        this.__count -= 1
        // 处理完成,返回 true
        return true
      }
      previous = current
      current = current.next
    }
    // 查找到最后没有找到指定数据的元素,返回 false
    return false

    // 注:
    // 也可以通过调用 indexOf 获取下标后再调用 removeAt 来实现
    // 只是返回值会不同,看实际需要
  }

  /**
   * 删除指定位置节点
   */
  LinkedList.prototype.removeAt = function (position) {
    // 1.边界检查
    if (position < 0 || position >= this.__count) return false

    var index = 0
    var previous = null
    var current = this.__head

    // 2.找到指定位置元素
    while (index++ < position) {
      previous = current
      current = current.next
    }
    // 3.使当前元素不再被引用
    previous.next = current.next

    // 4.内部计数减1
    this.__count -= 1

    return current.data
  }

  /**
   * 更新节点
   */
  LinkedList.prototype.update = function (position, data) {
    // 1.边界检查
    if (position < 0 || position >= this.__count) return false

    var current = this.__head
    var index = 0

    // 2.找到指定位置元素
    while (index++ < position) {
      current = current.next
    }
    // 3.修改当前元素数据
    current.data = data

    // 4.修改完成,返回 true
    return true
  }

  /**
   * 获取指定位置节点
   */
  LinkedList.prototype.getItem = function (position) {
    // 边界检查
    if (position < 0 || position >= this.__count) return

    var index = 0
    var current = this.__head

    while (index < position) {
      current = current.next
      index += 1
    }
    return current.data
  }

  /**
   * 获取节点位置下标
   */
  LinkedList.prototype.indexOf = function (data) {
    var current = this.__head
    var index = 0

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

  /**
   * 获取链表长度
   */
  LinkedList.prototype.size = function () {
    return this.__count
  }

  /**
   * 是否为空链表
   */
  LinkedList.prototype.isEmpty = function () {
    return this.__count === 0
  }

  /**
   * 清空链表
   */
  LinkedList.prototype.clear = function () {
    this.__head = null
    this.__count = 0
  }

  LinkedList.prototype.toString = function () {
    var str = '[HEAD] -> '
    var current = this.__head
    while (current) {
      str += current.toString() + ' -> '
      current = current.next
    }
    if (str === '[HEAD] -> ') {
      str = '[HEAD] -> Null'
    }
    return str
  }
}

4. test

// ---------------------------------------------
// Test: LinkedList
// ---------------------------------------------
console.log('----Test: LinkedList----')

var lst = new LinkedList()

lst.append('a')
lst.append('b')
lst.append('c')
console.log(lst.toString())
lst.insert(1, 'insert-1')
console.log(lst.toString())
lst.insert(4, 'insert-4')
console.log(lst.toString())
lst.insert(0, 'insert-0')
console.log(lst.toString())
lst.remove('c')
console.log(lst.toString(), 'remove-c')
console.log('indexOf-b : ', lst.indexOf('b'))
lst.update(3, 'b-updated')
console.log('update-b : ', lst.toString())
lst.removeAt(3)
console.log('after removeAt(3) : ', lst.toString())
lst.clear()
console.log('after clear : ', lst.toString())

View the output:

----Test: LinkedList----
[HEAD] -> a -> b -> c -> 
[HEAD] -> a -> insert-1 -> b -> c -> 
[HEAD] -> a -> insert-1 -> b -> c -> insert-4 -> 
[HEAD] -> insert-0 -> a -> insert-1 -> b -> c -> insert-4 -> 
[HEAD] -> insert-0 -> a -> insert-1 -> b -> insert-4 ->  remove-c
indexOf-b :  3
update-b :  [HEAD] -> insert-0 -> a -> insert-1 -> b-updated -> insert-4 -> 
after removeAt(3) :  [HEAD] -> insert-0 -> a -> insert-1 -> insert-4 -> 
after clear :  [HEAD] -> Null

The result is correct.
knock off.


Npm do a toolkit data-struct-js,
based on JavaScript data structures ES6 implemented,
although the small wheels rarely used,
perhaps for beginners to learn JavaScript will be a little help.
Simply install what you can, interested can also go to
GitHub / Gitee look at the source code. (Star Table support ~)

npm install data-struct-js --save-dev

https://github.com/CoderMonkie/data-struct-js
https://gitee.com/coder-monkey/data-struct-js

Finally, thank you for reading and support ~


-end-

Guess you like

Origin www.cnblogs.com/CoderMonkie/p/js-data-struct-linkedlist.html