• 数据结构——双向链表(双向连接的图解、双向链表的创建、操作双向链表)


    目录

    一、双向链表

    二、双向连接的图解:

    三、双向链表的创建

    四、操作双向链表


    一、双向链表

    - 单向链表:

      - 只能从头遍历到尾或者从尾遍历到头(一般从头到尾)
      - 也就是链表相连的过程是单向的. 实现的原理是上一个链表中有一个指向下一个的引用.
      - 单向链表有一个比较明显的缺点: 
        - 我们可以轻松的到达下一个节点, 但是回到前一个节点是很难的. 但是, 在实际开发中, 经常会遇到需要回到上一个节点的情况
        - 举个例子: 假设一个文本编辑用链表来存储文本. 每一行用一个String对象存储在链表的一个节点中. 当编辑器用户向下移动光标时, 链表直接操作到下一个节点即可. 但是当用于将光标向上移动呢? 这个时候为了回到上一个节点, 我们可能需要从first开始, 依次走到想要的节点上.

    - 双向链表

      - 既可以从头遍历到尾, 又可以从尾遍历到头
      - 也就是链表相连的过程是双向的. 那么它的实现原理, 你能猜到吗?
      - 一个节点既有向前连接的引用, 也有一个向后连接的引用.
      - 双向链表可以有效的解决单向链表中提到的问题.
      - 双向链表有什么缺点呢? 
        - 每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 也就是实现起来要困难一些
        - 并且相当于单向链表, 必然占用内存空间更大一些.
        - 但是这些缺点和我们使用起来的方便程度相比, 是微不足道的.

     

    二、双向连接的图解:

     

    三、双向链表的创建

    1. // 创建双向链表的构造函数
    2. function DoublyLinkedList() {
    3. // 创建节点构造函数
    4. function Node(element) {
    5. this.element = element
    6. this.next = null
    7. this.prev = null // 新添加的
    8. }
    9. // 定义属性
    10. this.length = 0
    11. this.head = null
    12. this.tail = null // 新添加的
    13. // 定义相关操作方法
    14. }

    - 基本思路和单向链表比较相似, 都是创建节点结构函数以及定义一些属性和方法.
    - 只是Node中添加了一个this.prev属性, 该属性用于指向上一个节点.
    - 另外属性中添加了一个this.tail属性, 该属性指向末尾的节点

    四、操作双向链表

    1、尾部追加数据

    2、正向反向遍历

    3、任意位置插入

    4、位置移除数据

    5、获取元素位置

    6、根据元素删除

    7、判断是否为空

     8、获取链表长度

    9、获取第一个元素

    10、获取最后一个元素

    1. class Dnode {
    2. constructor(data) {
    3. this.prev = null;
    4. this.data = data;
    5. this.next = null;
    6. }
    7. }
    8. class DoubleLinkList {
    9. constructor() {
    10. this.head = null;
    11. this.tail = null;
    12. this.len = 0
    13. }
    14. // 1.尾部添加节点
    15. append(ele) {
    16. let newnode = new Dnode(ele);
    17. // console.log(newnode);
    18. if (this.len == 0) { //空链表
    19. this.head = newnode;
    20. this.tail = newnode;
    21. } else {
    22. newnode.prev = this.tail; //新节点.prev = 尾节点
    23. this.tail.next = newnode; //尾节点.next指向新节点
    24. this.tail = newnode; //将尾节点指向新节点
    25. }
    26. this.len++;
    27. }
    28. // 2.向指定位置插入元素
    29. insert(position, ele) {
    30. // 位置是否合法
    31. if (position < 0 || position > this.len || !Number.isInteger(position)) {
    32. return false
    33. }
    34. let newnode = new Dnode(ele);
    35. if (position == 0) {
    36. if (this.len == 0) { //空链表
    37. this.head = newnode;
    38. this.tail = newnode;
    39. } else {
    40. newnode.next = this.head; //新节点.next指向头节点
    41. this.head.prev = newnode; //头节点.prev指向新节点
    42. this.head = newnode; //头节点指向新节点
    43. }
    44. this.len++
    45. } else if (position == this.len) {
    46. this.append(ele)
    47. } else {
    48. let current = this.head,
    49. index = 0;
    50. while (index < position - 1) {
    51. current = current.next;
    52. index++;
    53. }
    54. // 1.将新节点练上去
    55. newnode.prev = current;
    56. newnode.next = current.next;
    57. // 2.
    58. current.next = newnode;
    59. newnode.next.prev = newnode;
    60. this.len++;
    61. }
    62. }
    63. // 3.移除指定位置的元素
    64. removeAt(position) {
    65. // 位置是否合法
    66. if (position < 0 || position > this.len - 1 || !Number.isInteger(position)) {
    67. return false
    68. }
    69. if (this.len == 0) { //空链表
    70. return
    71. } else {
    72. if (position == 0) {
    73. if (this.len == 1) {
    74. this.head = null;
    75. this.tail = null;
    76. } else {
    77. this.head = this.head.next;
    78. this.head.prev = null;
    79. }
    80. } else if (position == this.len - 1) {
    81. this.tail = this.tail.prev;
    82. this.tail.next = null;
    83. } else {
    84. let current = this.head,
    85. index = 0;
    86. while (index < position - 1) {
    87. current = current.next;
    88. index++
    89. }
    90. current.next = current.next.next;
    91. current.next.prev = current
    92. }
    93. this.len--
    94. }
    95. }
    96. // 4.查找指定元素的位置
    97. indexOf(ele) {
    98. let current = this.head,
    99. index = 0;
    100. while (index < this.len) {
    101. if (current.data == ele) {
    102. return index
    103. } else {
    104. current = current.next;
    105. index++;
    106. }
    107. }
    108. return -1
    109. }
    110. // 5.移除指定元素
    111. remove(ele) {
    112. let index = this.indexOf(ele)
    113. this.removeAt(index)
    114. }
    115. // 6.正向遍历
    116. toAfterString() {
    117. let current = this.head,
    118. index = 0,
    119. res = "";
    120. while (index < this.len) {
    121. res += "-" + current.data;
    122. current = current.next;
    123. index++;
    124. }
    125. return res.slice(1)
    126. }
    127. // 7.反向遍历
    128. toBeforeString() {
    129. let current = this.tail,
    130. index = this.len - 1,
    131. res = "";
    132. while (index >= 0) {
    133. res += "-" + current.data;
    134. current = current.prev;
    135. index--;
    136. }
    137. return res.slice(1)
    138. }
    139. }
    140. let dlist = new DoubleLinkList();
    141. for (let i = 0; i < 9; i++) {
    142. dlist.append(i)
    143. }
    144. dlist.remove(6)
    145. console.log(dlist.toAfterString());
    146. console.log(dlist.toBeforeString());
  • 相关阅读:
    AD教程 (八)器件的复制和对齐
    LeetCode反转链表
    【MyBatis框架】核心配置文件讲解
    【每日五题】202203
    Vue 关键概念介绍
    【JavaWeb】火车票管理系统 (三)用户注册-最终版
    C++学习之旅
    【Javascript】编写⼀个函数,排列任意元素个数的数字数组,按从⼩到⼤顺序输出
    引导滤波融合matlab
    three.js webgl_tiled_forward 例子分析
  • 原文地址:https://blog.csdn.net/qq_52301431/article/details/126483615