https://cloud.tencent.com/developer/article/1114246

链表存储有序的元素的集合,但是和数组不同的是,链表中的元素在内存中的存储并不是连续的。每一个链表元素都包含了一个存储元素本身的节点一个指向下一个元素的引用。看起来就像这样:

  相对于传统的数组,链表的一个好处就是增删的时候无需移动其它元素,只要更改指针的指向就可以了。但是缺点就是如果想要访问链表中的元素,需要从头开始循环迭代到你想要的元素。

  1. function LinkedList() {
  2. // Node辅助类,表示要加入列表的项,element是即将添加到列表的值,next是指向列表中下一个节点项的指针
  3. let Node = function (element) {
  4. this.element = element
  5. this.next = null
  6. }
  7. let length = 0
  8. let head = null
  9. // 向链表尾部追加元素
  10. this.append = function (element) {
  11. let node = new Node(element)
  12. let current
  13. if (head === null) { // 列表中第一个节点
  14. head = node
  15. } else {
  16. current = head
  17. while (current.next) {
  18. current = current.next // 找到最后一项,是null
  19. }
  20. current.next = node // 给最后一项赋值
  21. }
  22. length++ // 更新列表的长度
  23. }
  24. // 从链表中移除指定位置元素
  25. this.removeAt = function (position) {
  26. if (position > -1 && position < length) { // 值没有越界
  27. let current = head
  28. let previous, index = 0
  29. if (position === 0) { // 移除第一项
  30. head = current.next
  31. } else {
  32. while (index++ < position) {
  33. previous = current
  34. current = current.next
  35. }
  36. previous.next = current.next // 将previous与current的下一项连接起来,跳过current,从而移除
  37. }
  38. length-- // 更新列表的长度
  39. return current.element
  40. } else {
  41. return null
  42. }
  43. }
  44. // 在链表任意位置插入一个元素
  45. this.insert = function (position, element) {
  46. if (position >= 0 && position <= length) { // 检查越界值
  47. let node = new Node(element),
  48. current = head,
  49. previous,
  50. index = 0
  51. if (position === 0) { // 在第一个位置添加
  52. node.next = current
  53. head = node
  54. } else {
  55. while (index++ < position) {
  56. previous = current
  57. current = current.next
  58. }
  59. node.next = current // 在previous与current的下一项之间插入node
  60. previous.next = node
  61. }
  62. length++
  63. return true
  64. } else {
  65. return false
  66. }
  67. }
  68. // 把链表内的值转换成一个字符串
  69. this.toString = function () {
  70. let current = head,
  71. string = ''
  72. while (current) {
  73. string += current.element + ' '
  74. current = current.next
  75. }
  76. return string
  77. }
  78. // 在链表中查找元素并返回索引值
  79. this.indexOf = function (element) {
  80. let current = head,
  81. index = 0
  82. while (current) {
  83. if (element === current.element) {
  84. return index
  85. }
  86. index++
  87. current = current.next
  88. }
  89. return -1
  90. }
  91. // 从链表中移除指定元素
  92. this.remove = function (element) {
  93. let index = this.indexOf(element)
  94. return this.removeAt(index)
  95. }
  96. this.isEmpty = function () {
  97. return length === 0
  98. }
  99. this.size = function () {
  100. return length
  101. }
  102. this.getHead = function () {
  103. return head
  104. }
  105. }
  106. let list = new LinkedList()
  107. list.append(1)
  108. list.append(2)
  109. console.log(list.toString()) // 1 2
  110. list.insert(0, 'hello')
  111. list.insert(1, 'world')
  112. console.log(list.toString()) // hello world 1 2
  113. list.remove(1)
  114. list.remove(2)
  115. console.log(list.toString()) // hello world
单链表有一个变种 - 循环链表,最后一个元素指向下一个元素的指针,不是引用null,而是指向第一个元素,只需要修改下最后的next指向为head即可。
 
双向链表与单链表的区别在于,在单链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素。
  双向链表提供了两种迭代列表的方法:从头到尾,或则反过来。在单链表中,如果我们在迭代列表中错过了要找的元素,就需要回到列表的起点,重新开始迭代,这是双向列表的优点。
  双向链表与单向链表的实现类似,需要同时控制next、prev两个指针,同时需要增加尾引用tail。
  1. function DoubleLinkedList() {
  2. // Node辅助类,表示要加入列表的项,element是即将添加到列表的值,next是指向列表中下一个节点项的指针
  3. let Node = function (element) {
  4. this.element = element
  5. this.prev = null // 新增一个向前的指针
  6. this.next = null
  7. }
  8. let length = 0
  9. let head = null
  10. let tail = null // 新增一个尾引用
  11. // 向链表尾部追加元素
  12. this.append = function (element) {
  13. let node = new Node(element)
  14. let current
  15. if (head === null) { // 列表中第一个节点
  16. head = node // head与tail是同一个元素
  17. tail = node
  18. } else {
  19. current = head
  20. while (current.next) {
  21. current = current.next // 找到最后一项,是null
  22. }
  23. current.next = node // 给最后一项赋值
  24. node.prev = current
  25. tail = node // 修改尾引用
  26. }
  27. length++ // 更新列表的长度
  28. }
  29. // 从链表中移除指定位置元素
  30. this.removeAt = function (position) {
  31. if (position > -1 && position < length) { // 值没有越界
  32. let current = head
  33. let previous,
  34. index = 0
  35. if (position === 0) { // 移除第一项
  36. head = current.next
  37. if (length === 1) { // 只有一项
  38. tail = null
  39. } else {
  40. head.prev = null
  41. }
  42. } else if (position === length - 1) { // 移除最后一项
  43. current = tail
  44. tail = current.prev
  45. tail.next = null
  46. }
  47. else {
  48. while (index++ < position) {
  49. previous = current
  50. current = current.next
  51. }
  52. previous.next = current.next // 将previous与current的下一项连接起来,跳过current,从而移除
  53. current.next.prev = previous
  54. }
  55. length-- // 更新列表的长度
  56. return current.element
  57. } else {
  58. return null
  59. }
  60. }
  61. // 在链表任意位置插入一个元素
  62. this.insert = function (position, element) {
  63. if (position >= 0 && position <= length) { // 检查越界值
  64. let node = new Node(element),
  65. current = head,
  66. previous,
  67. index = 0
  68. if (position === 0) { // 在第一个位置添加
  69. if (!head) {
  70. head = node
  71. tail = node
  72. }else {
  73. node.next = current
  74. current.prev = node
  75. head = node
  76. }
  77. node.next = current
  78. head = node
  79. } else if (position === length) {
  80. current = tail
  81. current.next = node
  82. node.prev = current
  83. tail = node
  84. } else {
  85. while (index++ < position) {
  86. previous = current
  87. current = current.next
  88. }
  89. node.next = current // 在previous与current的下一项之间插入node
  90. previous.next = node
  91. current.prev = node
  92. node.prev = previous
  93. }
  94. length++
  95. return true
  96. } else {
  97. return false
  98. }
  99. }
  100. // 把链表内的值转换成一个字符串
  101. this.toString = function () {
  102. let current = head,
  103. string = ''
  104. while (current) {
  105. string += current.element + ' '
  106. current = current.next
  107. }
  108. return string
  109. }
  110. // 在链表中查找元素并返回索引值
  111. this.indexOf = function (element) {
  112. let current = head,
  113. index = 0
  114. while (current) {
  115. if (element === current.element) {
  116. return index
  117. }
  118. index++
  119. current = current.next
  120. }
  121. return -1
  122. }
  123. // 从链表中移除指定元素
  124. this.remove = function (element) {
  125. let index = this.indexOf(element)
  126. return this.removeAt(index)
  127. }
  128. this.isEmpty = function () {
  129. return length === 0
  130. }
  131. this.size = function () {
  132. return length
  133. }
  134. this.getHead = function () {
  135. return head
  136. }
  137. }
  138. let list = new DoubleLinkedList()
  139. list.append(1)
  140. list.append(2)
  141. console.log(list.toString()) // 1 2
  142. list.insert(0, 'hello')
  143. list.insert(1, 'world')
  144. console.log(list.toString()) // hello world 1 2
  145. list.remove(1)
  146. list.remove(2)
  147. console.log(list.toString()) // hello world
 

JS实现单向链表、双向链表、循环链表的更多相关文章

  1. 原生JS实现单向链表

    1.前言 用JS实现一个简单的单向链表,并完成相关的功能 2.功能说明 push(value):从链表尾部添加一个新的节点 insertAfer(value,item):向链表中的item节点之后插入 ...

  2. 单链表、循环链表的JS实现

    数据结构系列前言: 数据结构作为程序员的基本知识,需要我们每个人牢牢掌握.近期我也展开了对数据结构的二次学习,来弥补当年挖的坑......   当时上课的时候也就是跟着听课,没有亲自实现任何一种数据结 ...

  3. 《Java数据结构》链表结构(单向链表,双向链表)

    单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始:链表是使用指针进行构造的列表:又称为结点列表,因为链表是由一个个结点组装起来的:其中每个结点都有指 ...

  4. Python 单向链表、双向链表

    用面向对象实现Linkedlist链表 单向链表实现append.iternodes 双向链表实现append.pop.insert.remove.iternodes 单向链表与双向链表 单向链表: ...

  5. Python链表的实现与使用(单向链表与双向链表)

    参考[易百教程]用Python实现链表及其功能 """ python链表的基本操作:节点.链表.增删改查 """ import sys cl ...

  6. 玩转C线性表和单向链表之Linux双向链表优化

    前言: 这次介绍基本数据结构的线性表和链表,并用C语言进行编写:建议最开始学数据结构时,用C语言:像栈和队列都可以用这两种数据结构来实现. 一.线性表基本介绍 1 概念: 线性表也就是关系户中最简单的 ...

  7. 用Python写单向链表和双向链表

    链表是一种数据结构,链表在循环遍历的时候效率不高,但是在插入和删除时优势比较大. 链表由一个个节点组成. 单向链表的节点分为两个部分:存储的对象和对下一个节点的引用.注意是指向下一个节点. 而双向链表 ...

  8. Java-链表(单向链表、双向链表)

    Java-链表 1.什么是链表? 2.链表的特点是什么? 3.链表的实现原理? 4.如何自己写出一个链表? 1.什么是链表? 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过 ...

  9. MySQL记录之间是单向链表还是双向链表?

    前言 本文的观点是基于MySQL使用Innodb存储引擎的情况下进行的! 很多渠道说:MySQL数据按照主键大小依次排列,记录之间是双向链表连起来.如果说我告诉你这种说法很大程度上是错的,你肯定说我在 ...

随机推荐

  1. Depth Buffer

    Up until now there is only one type of output buffer you've made use of, the color buffer. This chap ...

  2. SelectObject()函数详解

    SelectObject 把一个对象(位图.画笔.画刷等)选入指定的设备描述表.新的对象代替同一类型的老对象. HGDIOBJ SelectObject(   HDC hdc,          // ...

  3. Golang : pflag 包简介

    笔者在前文中介绍了 Golang 标准库中 flag 包的用法,事实上有一个第三方的命令行参数解析包 pflag 比 flag 包使用的更为广泛.pflag 包的设计目的就是替代标准库中的 flag ...

  4. JavaScript 检验变量

    创建: 2019/02/20 迁入: 删除[WIP]标签(因为随时更新, 不存在完成不完成)   从[JavaScript 式与运算符]迁入typeof 更新: 2019/03/25 补充静态变量与参 ...

  5. IT兄弟连 Java语法教程 变量1

    什么是变量 在Java程序中,变量是基本的存储单元.是在程序运行中值可以改变的一块内存区域.变量是通过标识符(变量名).变量类型及可选的初始化器来定义的,此外,所有的变量都有作用域,作用域定义了变量的 ...

  6. SpringMVC 控制器写多个方法(非注解方式)

    Controller类有两种方法 1,implements Controller(实现Controller接口) 2,extends MultiActionController(继承 MultiAct ...

  7. Opencv级联分类器实现人脸识别

    在本章中,我们将学习如何使用OpenCV使用系统相机捕获帧.org.opencv.videoio包的VideoCapture类包含使用相机捕获视频的类和方法.让我们一步一步学习如何捕捉帧 - 第1步: ...

  8. CSS样式之操作属性二

    ********css样式之属性操作******** 一.文本属性 1.text-align:cnter 文本居中 2.line heigth 垂直居中 :行高,和高度对应 3.vertical-al ...

  9. MySQL 查询练习记录

    MySQL 查询练习记录 最近在复习mysql,在b站上找了一个感觉还不错的视频,把视频中查询练习相关的内容记录了下来,以便自己日后查阅和复习. 视频连接:https://www.bilibili.c ...

  10. Codeforces Round #558 (Div. 2)

    目录 Codeforces Round #558 (Div. 2) 题解 A Eating Soup B Cat Party C Power Transmission D Mysterious Cod ...