【JS数据结构】双向链表

目录

一. 认识双向链表

1.单向链表

1.1 概念

1.2 实现的原理

1.3 缺点

2.双向链表

2.1 概念

2.2 实现的原理

2.3 缺点

2.4 双向连接的特点图解

二.双向链表的创建

1.创建一个双向链表的类

2.双向链表的常见操作

2.1 append(element)尾部追加数据

2.2 将链表转成字符串形式

2.3 insert(position, element)任意位置插入

2.4 get(position)获取对应位置的元素

2.5 indexOf(element)返回元素在列表中的索引

2.6 update(position, data)修改某个位置的元素

2.7 removeAt(position)从列表的特定位置移除一项

2.8 remove(data)从列表中移除一项

2.9 isEmpty()判空

2.10 size()获取链表长度

2.11 gatHead()获取链表第一个元素

2.12 getTail()获取链表最后一个元素 

三. 完整代码


一. 认识双向链表

1.单向链表

1.1 概念

只能从头遍历到尾或者从尾遍历到头(一般从头到尾),即链表相连的过程是单向的.

1.2 实现的原理

上一个链表中有一个指向下一个的引用.

1.3 缺点

我们可以轻松的到达下一个节点, 但是回到前一个节点是很难的. 但是, 在实际开发中, 经常会遇到需要回到上一个节点的情况

举个例子:

假设一个文本编辑器用链表来存储文本. 每一行用一个String对象存储在链表的一个节点中.

当编辑器用户向下移动光标时, 链表直接操作到下一个节点即可.

但是当用于将光标向上移动呢? 这个时候为了回到上一个节点, 我们可能需要从first开始, 依次走到想要的节点上.

2.双向链表

2.1 概念

既可以从头遍历到尾, 又可以从尾遍历到头,即链表相连的过程是双向的. 双向链表可以有效的解决单向链表中提到的问题.

2.2 实现的原理

一个节点既有向前连接的引用, 也有一个向后连接的引用.

2.3 缺点

每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 也就是实现起来要困难一些

并且相当于单向链表, 必然占用内存空间更大一些.

但是这些缺点和我们使用起来的方便程度相比, 是微不足道的.

2.4 双向连接的特点图解

  • head:指向第一个节点;tail:指向最后一个节点
  • 每个节点node都由三个部分组成:prev:指向前一个节点;next:指向后一个节点;item:保存的元素
  • 双向链表的第一个节点的prev是null,最后的节点的next是null。

二.双向链表的创建

1.创建一个双向链表的类

        // 封装双向链表
        function DoublyLinkedList() {
            // 内部类:节点类
            function Node(data) {
                this.data = data;
                this.prev = null;
                this.next = null;
            }

            // 属性
            this.head = null;
            this.tail = null;
            this.length = 0;

            // 常见的操作:方法
        }

代码解析:

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

2.双向链表的常见操作

双向链表的操作和单向链表的方法都是类似的.

只是在实现的过程中, 需要考虑更多节点之间的关系, 所以变得比单向链表复杂了一些.

  • append(element):向列表尾部添加一个新的项
  • insert(position, element):向列表的特定位置插入一个新的项。
  • get(position):获取对应位置的元素
  • indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
  • update(position, element):修改某个位置的元素
  • removeAt(position):从列表的特定位置移除一项。
  • remove(element):从列表中移除一项。
  • isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false.size():返回链表包含的元素个数。与数组的length属性类似。
  • size():获取链表长度。
  • toString0:由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。
  • forwardString():链表从后往前遍历,返回字符串形式。
  • backwordString():链表从前往后遍历,返回字符串形式。
  • gatHead():获取链表第一个元素

  • getTail()获取链表最后一个元素

2.1 append(element)尾部追加数据

封装代码:

            // 1.append(element)尾部追加数据
            DoublyLinkedList.prototype.append = function(data){
                // 根据元素创建节点
                var newNode = new Node(data)
                
                // 判断是否添加的是第一个节点
                if(this.length == 0){
                    this.head = newNode
                    this.tail = newNode
                }else{
                    this.tail.next = newNode
                    newNode.prev = this.tail
                    this.tail = newNode
                }
                this.length += 1
            }

代码解析:

  • 代码1部分不用多讲, 还是通过元素创建新的节点.
  • 代码2部分相比之前有一些复杂, 但是还是两种情况.
  • 情况一: 链表原来为空
    • 链表中原来如果没有数据, 那么直接让head和tail指向这个新的节点即可.
  • 情况二: 链表中已经存在数据
    • 因为我们是要将数据默认追加到尾部, 所以这个变得也很简单.
    • 首先tail中的next之前指向的是null. 现在应该指向新的节点newNode: this.tail.next = newNode
    • 因为是双向链表, 新节点的next/tail目前都是null. 但是作为最后一个节点, 需要有一个指向前一个节点的引用. 所以这里我们需要newNode.prev = this.tail
    • 因为目前newNod已经变成了最后的节点, 所以this.tail属性的引用应该指向最后: this.tail = newNode即可
  • 代码3部分不用多做解析, length需要+1

2.2 将链表转成字符串形式

链表的遍历:

  • 之前我们在单向链表中实现了一个toString方法, 它是一种正向的遍历.
  • 现在, 为了用户使用方便, 我们实现三个方法
    • forwardString:向前遍历转成字符串的方法
    • backwordString: 向后遍历转成字符串的方法
    • toString: 正向遍历转成字符串的方法

三个方法的代码封装:

            // 2.1 toString()方法
            DoublyLinkedList.prototype.toString = function () {
                return this.backwardString()
            }

            // 2.2 forwardString()方法
            DoublyLinkedList.prototype.forwardString = function () {
                // 定义变量
                var current = this.tail;
                var resultString = ""

                // 依次向前遍历,获取每一个节点
                while (current) {
                    resultString += current.data + " ";
                    current = current.prev; // 将current依次向前移,直至前面没有元素时,循环停止
                }

                return resultString;

            }

            // 2.3 backwardString()方法
            DoublyLinkedList.prototype.backwardString = function () {
                // 定义变量
                var current = this.head;
                var resultString = ""

                // 依次向后遍历,获取每一个节点
                while (current) {
                    resultString += current.data + " ";
                    current = current.next; // 将current依次向后移,直至后面没有元素时,循环停止
                }

                return resultString;
            }

测试方法:


                
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值