链表是一种线性结构,由一个一个的节点连接而成,具体结构如下:
    链表 - 图1
    可以看出,每个节点保存了两个值,一个值是节点自身的数据,另一个值是一个指针,指向下一个节点。通过next指针就可以将所有节点连接起来。节点的数据结构如下:

    1. class LinkNode {
    2. constructor (e = null, next = null) {
    3. this.e = e
    4. this.next = next
    5. }
    6. toString () { // 方便打印
    7. return this.e.toString()
    8. }
    9. }

    根据上述说明,可以写出链表的数据结构:

    1. class LinkedList {
    2. constructor () {
    3. this.dummyHead = new LinkNode() // 为了方便操作使用了一个虚拟头结点,这个节点的next指针指向头结点
    4. this.size = 0
    5. }
    6. getSize () {
    7. return this.size
    8. }
    9. isEmpty () {
    10. return this.size === 0
    11. }
    12. add (index, e) {
    13. if (index < 0 && index >= this.size) throw new RangeError('Add failed. Inlegal index')
    14. let prev = this.dummyHead
    15. for (let i = 0; i < index; i ++) {
    16. prev = prev.next
    17. }
    18. // const Node = new LinkNode(e)
    19. // Node.next = prev.next
    20. // prev.next = Node
    21. // 上面三行代码等价于下面这句
    22. prev.next = new LinkNode(e, prev.next)
    23. this.size ++
    24. }
    25. addFirst (e) {
    26. this.add(0, e)
    27. }
    28. addLast (e) {
    29. this.add(this.size, e)
    30. }
    31. get (index) {
    32. if (index < 0 || index >= this.size) throw new RangeError('Add failed. Inlegal index')
    33. let cur = this.dummyHead.next
    34. for (let i = 0; i < index; i ++) {
    35. cur = cur.next
    36. }
    37. return cur.e
    38. }
    39. getFirst () {
    40. return this.get(0)
    41. }
    42. getLast () {
    43. return this.get(this.size - 1)
    44. }
    45. set (index, e) {
    46. if (index < 0 || index >= this.size) throw new RangeError('Add failed. Inlegal index')
    47. let cur = this.dummyHead.next
    48. for (let i = 0; i < index; i ++) {
    49. cur = cur.next
    50. }
    51. cur.e = e
    52. }
    53. contains (e) {
    54. let cur = this.dummyHead.next
    55. while (cur !== null) {
    56. if (cur.e === e) return true
    57. cur = cur.next
    58. }
    59. return false
    60. }
    61. remove (index) {
    62. let prev = this.dummyHead
    63. for (let i = 0; i < index; i ++) {
    64. prev = prev.next
    65. }
    66. const node = prev.next
    67. prev.next = prev.next.next
    68. node.next = null
    69. this.size --
    70. }
    71. removeFirst () {
    72. this.remove(0)
    73. }
    74. removeLast () {
    75. this.remove(this.size - 1)
    76. }
    77. removeElement (e) {
    78. let cur = this.dummyHead
    79. while (cur.next) {
    80. if (cur.next.e === e) cur.next = cur.next.next
    81. else cur = cur.next
    82. }
    83. }
    84. toString () {
    85. let ret = ''
    86. let cur = this.dummyHead.next
    87. for (; cur !== null; cur = cur.next) {
    88. ret += cur.toString() + ' => '
    89. }
    90. ret += 'NULL'
    91. return ret
    92. }
    93. }
    94. module.exports = LinkedList

    也可以使用递归的方式:

    1. /**
    2. * 使用递归的方式
    3. */
    4. class LinkedList {
    5. constructor () {
    6. this.head = null
    7. this.size = 0
    8. }
    9. isEmpty () {
    10. return this.size === 0
    11. }
    12. getSize () {
    13. return this.size
    14. }
    15. get (index) {
    16. if (index < 0 || index > this.size) throw new RangeError('Get failed. Illegal index.')
    17. return this._get(index, this.head)
    18. }
    19. _get (index, node) {
    20. if (index === 0) return node.el
    21. return this._get(index - 1, node.next)
    22. }
    23. getFirst () {
    24. return this.get(0)
    25. }
    26. getLast () {
    27. return this.get(this.size)
    28. }
    29. set (index, el) {
    30. if (index < 0 || index >= this.size) throw new RangeError('Add failed. Illegal index.')
    31. this._set(index, el, this.head)
    32. }
    33. _set (index, el, node) {
    34. if (index === 0) {
    35. node.el = el
    36. return
    37. }
    38. this._set(index - 1, el, node.next)
    39. }
    40. setFirst (el) {
    41. this.set(0, el)
    42. }
    43. setLast (el) {
    44. this.set(this.size, el)
    45. }
    46. contains (el) {
    47. return this._contains(el, this.head)
    48. }
    49. _contains (el, node) {
    50. if (node === null) return false
    51. if (node.el === el) return true
    52. return this._contains(el, node.next)
    53. }
    54. add (el, index) {
    55. if (index < 0 || index > this.size) throw new RangeError('Add failed. Illegal index.')
    56. this.head = this._add(this.head, el, index)
    57. this.size ++
    58. }
    59. _add (node, el, index) {
    60. if (index === 0) return new LinkNode(el, node)
    61. node.next = this._add(node.next, el, index - 1)
    62. return node
    63. }
    64. addFirst (el) {
    65. this.add(el, 0)
    66. }
    67. addLast (el) {
    68. this.add(el, this.size)
    69. }
    70. remove (index) {
    71. if (index < 0 || index >= this.size) throw new RangeError('Add failed. Illegal index.')
    72. const ret = this._remove(index, this.head)
    73. this.head = ret.keys().next().value
    74. this.size --
    75. return ret.get(this.head)
    76. }
    77. _remove (index, node) {
    78. // 这里注意,由于需要返回被删除的节点的值,所以使用map进行保存
    79. if (index === 0) return new Map([[node.next, node.el]])
    80. const ret = this._remove(index - 1, node.next)
    81. node.next = ret.keys().next().value // 利用迭代器的特性取出key值
    82. return new Map([[node, ret.values().next().value]])
    83. }
    84. removeFirst () {
    85. return this.remove(0)
    86. }
    87. removeLast () {
    88. return this.remove(this.size - 1)
    89. }
    90. removeElement (el) {
    91. this.head = this._removeElement(el, this.head)
    92. }
    93. _removeElement (el, node) {
    94. if (node === null) return null
    95. node.next = this._removeElement(el, node.next) // 注意要先执行这一步求出移除el后子链表的头结点
    96. if (node.el === el) {
    97. this.size --
    98. return node.next
    99. }
    100. return node
    101. }
    102. toString () {
    103. let ret = '[LinkedList]: '
    104. let cur = this.head
    105. while (cur !== null) {
    106. ret += cur.toString() + ' => '
    107. cur = cur.next
    108. }
    109. ret += 'NULL'
    110. return ret
    111. }
    112. }
    113. module.exports = LinkedList

    需要注意的是,递归的方式是将链表分为头结点和子链表,以add为例,_add内部将链表拆分后,子链表的头结点即是head.next,而相对应的第index个节点子链表中则是第index - 1个节点。
    使用链表也可以实现栈和队列,且因为链表是动态的线性结构,因此不需要考虑内存的问题:
    实现栈:

    1. const LinkedList = require('./LinkedList')
    2. class Stack {
    3. constructor () {
    4. this.data = new LinkedList()
    5. }
    6. push (e) {
    7. this.data.addFirst(e)
    8. }
    9. pop () {
    10. return this.data.removeFirst()
    11. }
    12. peek () {
    13. return this.data.getFirst()
    14. }
    15. toString () {
    16. return `[Stack]: top ${this.data.toString()}`
    17. }
    18. }
    19. module.exports = Stack

    实现队列:

    1. class LinkNode {
    2. constructor (e = null, next = null) {
    3. this.e = e
    4. this.next = next
    5. }
    6. toString () {
    7. return this.e.toString()
    8. }
    9. }
    10. class Queue {
    11. constructor () {
    12. this.head = null
    13. this.tail = null
    14. this.size = 0
    15. }
    16. isEmpty () {
    17. return this.size === 0
    18. }
    19. enqueue (e) {
    20. if (this.size === 0) {
    21. this.head = this.tail = new LinkNode(e)
    22. } else {
    23. this.tail.next = new LinkNode(e)
    24. this.tail = this.tail.next
    25. }
    26. this.size ++
    27. }
    28. dequeue () {
    29. if (this.size === 0) throw new RangeError('cannot dequeue element from empty queue.')
    30. const ret = this.head
    31. this.head = this.head.next
    32. if (this.head === null) this.tail = null
    33. this.size --
    34. return ret
    35. }
    36. getFront () {
    37. return this.head
    38. }
    39. toString () {
    40. let ret = '[Queue]: front '
    41. let cur = this.head
    42. for (; cur !== null; cur = cur.next) {
    43. ret += cur.toString() + ' => '
    44. }
    45. ret += 'NULL tail'
    46. return ret
    47. }
    48. }
    49. module.exports = Queue