要存储多个元素,数组可能是最常用的数据结构。这种数据结构非常方便,提供了便利的语法来访问它的元素。然而,这种数据结构有一个缺点:(在大多数语言中)数组的大小是固定的,从数组的起点或中间插入或移除项的成本很高,因为需要移动元素。虽然 JavaScript 的 Array 类方法可以帮我们做这些事,但背后的情况同样是这样。

    链表存储有序的元素集合,但不同于数组,链表的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也成为指针或链接)组成。

    下图展示了一个链表的结构:

    单链表的实现 - 图1

    相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其它元素。然后,链表需要使用指针,因此实现链表时需要额外注意。

    数组的另一个细节是可以直接访问任何位置的任何元素,想要访问链表中间的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素。

    先规划一下链表将实现什么方法:

    • append:向列表尾部添加元素
    • insert:向列表任意位置添加元素
    • removeAt:从列表特定位置移除元素
    • remove:从列表中删除第一个与给定元素相匹配的元素
    • indexOf:返回给定元素在列表中的索引
    • isEmpty:查看列表是否为空
    • size:查看列表中的元素个数
    • getHead:获取列表中第一个元素
    • toString:以字符串的形式输出列表中的所有元素
    1. class Node {
    2. constructor(element) {
    3. // 要添加到列表中的值
    4. this.element = element
    5. // 指向列表中下一个节点项的指针
    6. this.next = null
    7. }
    8. }
    9. class LinkedList {
    10. constructor() {
    11. this.length = 0 // 列表元素的数量
    12. this.head = null // 第一个节点的引用
    13. }
    14. /**
    15. * 向列表尾部添加元素
    16. * @param {any} element
    17. */
    18. append(element) {
    19. let node = new Node(element) // 创建一个 Node
    20. if (this.head === null) {
    21. // 如果列表为空,则将添加的元素设置为列表的第一个元素
    22. this.head = node
    23. } else {
    24. // 向不为空的列表添加元素
    25. // 要向列表的尾部添加一个元素,首先需要找到最后一个元素
    26. let current = this.head // 用于存储列表的最后一个元素
    27. while (current.next) {
    28. // 循环列表,直到找到最后一项
    29. // 最后一个元素的 next 为 null,因此找到最后一个元素之后将不会再进入循环
    30. current = current.next
    31. }
    32. // 找到最后一项,将其 next 赋为 node,建立链接
    33. current.next = node
    34. }
    35. // 列表的长度+1
    36. this.length++
    37. }
    38. /**
    39. * 向列表的特定位置插入一个新元素
    40. * @param {number} position
    41. * @param {any} element
    42. */
    43. insert(position, element) {
    44. // 插入的有效位置为 0 至 this.length
    45. if (position < 0 || position > this.length) {
    46. return false // 表示插入失败
    47. }
    48. let node = new Node(element)
    49. if (position === 0) {
    50. // 在第一个位置添加元素
    51. node.next = this.head
    52. this.head = node
    53. } else {
    54. let previous = null // 用于存储位置为 position 前一位的元素
    55. let current = this.head // 用于存储位置为 position 的元素
    56. for (let i = 1; i <= position; i++) {
    57. previous = current
    58. current = current.next
    59. }
    60. previous.next = node
    61. node.next = current
    62. }
    63. this.length++
    64. return true
    65. }
    66. /**
    67. * 从列表的特定位置移除一项
    68. * @param {number} position
    69. */
    70. removeAt(position) {
    71. // 检查越界值,当 position 在 0 到 this.length - 1 才是有效位置
    72. if (position < 0 || position >= this.length) {
    73. return null // 表示没有移除元素
    74. }
    75. let removedElement // 用于存储被移除的元素
    76. if (position === 0) {
    77. removedElement = this.head // 记录被移除的元素
    78. // 如果删除的是列表的第一个元素,只需要将 this.head 指向第二个元素即可
    79. this.head = this.head.next
    80. } else {
    81. let current = this.head // 用于存储 position 所指的元素
    82. let previous = null // 用于存储 position 前一位元素
    83. // 找出 position 所指的元素,以及 position 的前一个元素
    84. // 当跳出循环之后,current 为 position 所对应的元素
    85. for (let i = 1; i <= position; i++) {
    86. previous = current
    87. current = current.next
    88. }
    89. removedElement = current // 记录被移除的元素
    90. // 将 prevous 与 current 的下一项链接起来:跳过 current,从而移除它
    91. previous.next = current.next
    92. }
    93. this.length--
    94. return removedElement
    95. }
    96. /**
    97. * 从列表中删除第一个与给定元素相匹配的元素
    98. * @param {any} element
    99. */
    100. remove(element) {
    101. let result = false
    102. if (this.head.element === element) {
    103. // 移除的项为第一个元素
    104. this.head = this.head.next
    105. result = true // 表示删除元素成功
    106. } else {
    107. // 移除的项并非第一个元素
    108. let previous = this.head
    109. let current = this.head.next
    110. while (current) {
    111. if (current.element === element) {
    112. previous.next = current.next
    113. result = true // 表示删除元素成功
    114. }
    115. previous = current
    116. current = current.next
    117. }
    118. }
    119. return result
    120. }
    121. /**
    122. * 返回元素在列表的索引
    123. * @param {any} element
    124. */
    125. indexOf(element) {
    126. let current = this.head
    127. let index = 0
    128. while (current) {
    129. if (element === current.element) {
    130. return index
    131. }
    132. index++
    133. current = current.next
    134. }
    135. return -1
    136. }
    137. /**
    138. * 如果链表中不包含任何元素,返回 true,否则返回 false
    139. */
    140. isEmpty() {
    141. return this.length === 0
    142. }
    143. /**
    144. * 返回链表包含的元素个数
    145. */
    146. size() {
    147. return this.length
    148. }
    149. /**
    150. * 获取第一个元素
    151. */
    152. getHead() {
    153. return this.head
    154. }
    155. /**
    156. * 由于列表项使用了 Node 类,需要重写继承自 JavaScript 对象默认的 toString 方法,
    157. * 让其只输出元素的值。
    158. */
    159. toString() {
    160. let current = this.head
    161. let string = ''
    162. while (current) {
    163. string += current.element + (current.next ? ',' : '')
    164. current = current.next
    165. }
    166. return string
    167. }
    168. }