双向链表及双向链表的常见操作和用js封装一个双向链表

news2025/1/16 15:05:56

书接上回,上一篇文章讲了单向链表以及用 js 封装一个单向链表,所以这节将介绍双向链表以及用 js 封装一个双向链表。待会我也会继续在文章后面附上视频学习链接地址,大家想学习的可以去看看

 一、认识双向链表

首先来认识一下什么是双向链表,以及单向链表与双向链表的区别和它们各自的缺点

双向链表的图解

二、双向链表的常见操作 

 

三、双向链表的封装

1、append 方法

// 封装 append 追加方法
DoublyLinkedList.prototype.append = function() {
    // 1、根据data创建节点
    var newNode = new Node(data)

    // 2、判断添加的是否是第一个节点
    if (this.length == 0) {
        this.head = newNode
        this.tail = newNode
    } else {
        newNode.prev = this.tail
        this.tail.next = newNode
        this.tail = newNode
    }

    // 3、length + 1
    this.length += 1
}

2、toString 方法

// 2、封装将链表转成字符串形式的方法
// 2.1 toString 方法
DoublyLinkedList.prototype.toString = function() {
    return this.backwordString()
}

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

    // 依次向前遍历,获取每一个节点
    while (current) {
        resultString += current.data + " "
        current = current.prev
    }

    // 返回最终的值
    return resultString
}

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

    // 依次向后遍历,获取每一个节点
    while (current) {
        resultString += current.data + " "
        current = current.next
    }

    // 返回最终的值
    return resultString
}

3、insert 方法

// 3、封装 insert 插入方法
DoublyLinkedList.prototype.insert = function(position, data) {
    // 越界判断
    if (position < 0 || position > this.length) return false

    // 根据 data 创建新的节点
    var newNode = new Node(data)

    // 判断原来的列表是否为空
    if (this.length == 0) {
        this.head = newNode
        this.tail = newNode
    } else {
        if (position == 0) { // 如果插入到开头
            this.head.prev = newNode // 将原来节点的前一个节点指向新得节点
            newNode.next = this.head // 将新节点的下一个节点指向原来的节点
            this.head = newNode // 将头结点指向新插入的节点
        } else if (position == this.length) { // 如果插入到结尾
            newNode.prev = this.tail // 将新插入节点的前一个节点指向原来的最后一个节点
            this.tail.next = newNode // 将原来最后一个节点的下一个节点指向新插入的节点
            this.tail = newNode // 将尾节点指向新插入的节点
        } else { // 如果插入到中间
            var current = this.head
            var index = 0

            while (index++ < position) {
                current = current.next
            }

            // 修改指针
            newNode.next = current // 将新插入节点的下一个节点指向原来位置上的节点
            newNode.prev = current.prev // 将新插入的节点的上一个节点指向原来位置上节点的上一个节点
            current.prev.next = newNode // 将原来位置上节点的上一个节点的下一个节点指向新的节点
            current.prev = newNode // 将原来位置上节点的上一个节点指向新的节点
        }
    }

    // length + 1
    this.length += 1

    return true
}

图解中间插入时的情况 

4、get 方法

// 方法一 : 从前往后查找,效率低下
// 4、封装 get 获取方法
DoublyLinkedList.prototype.get = function(position) {
    // 越界判断
    if (position < 0 || position >= this.length) return null

    // 获取对应的data
    var current = this.head // 定义当前值为头节点
    var index = 0 // 定义索引值为0

    while (index++ < position) { // 循环找到需要的 position 对应位置的值
        current = current.next
    }

    return current.data
}

// 方法二 : 与长度的一般进行比较,小于的时候从前往后找,大于的时候从后往前找,效率比较高
// 4、封装 get 获取方法
DoublyLinkedList.prototype.get = function(position) {
    // 越界判断
    if (position < 0 || position >= this.length) return null

    if (position <= this.length / 2) { // 如果要查找的位置小于长度的一半
        // 获取对应的data
        var current = this.head // 定义当前值为头节点
        var index = 0 // 定义索引值为0

        while (index++ < position) { // 循环找到需要的 position 对应位置的值
            current = current.next // 让current指向他的后一个节点
        }

        return current.data
    } else if (position > this.length / 2) { // 如果要查找的位置大于长度的一半
        var current = this.tail // 定义当前值为尾节点
        var index = this.length - 1 // 定义索引值为长度减一

        while (index-- > position) { // 循环找到需要的 position 对应位置的值
            current = current.prev // 让current指向他的前一个节点
        }

        return current.data
    }
}

5、indexOf 方法

// 5、封装 indexOf 返回指定数据的下标值方法
DoublyLinkedList.prototype.indexOf = function(data) {
    // 定义变量
    var current = this.head
    var index = 0

    // 查找current的data与传入值的data是否相等
    while (current) {
        if (current.data == data) {
            return index
        }
        index += 1
        current = current.next
    }

    return -1
}

6、update 方法

// 方法一 : 从前往后查找,效率低下
// 6、封装 update 更新指定位置数据的方法
DoublyLinkedList.prototype.update = function(position, newData) {
    // 越界判断
    if (position < 0 || position > this.length) return false

    // 定义变量
    var current = this.head
    var index = 0

    while (index++ < position) {
        current = current.next
    }

    current.data = newData
    return true
}

// 方法二 : 与长度的一般进行比较,小于的时候从前往后找,大于的时候从后往前找,效率比较高
// 6、封装 update 更新指定位置数据的方法
DoublyLinkedList.prototype.update = function(position, newData) {
    // 越界判断
    if (position < 0 || position > this.length) return false

    if (position <= this.length / 2) {
        // 定义变量
        var current = this.head
        var index = 0

        while (index++ < position) {
            current = current.next
        }

        current.data = newData
        return true
    } else if (position > this.length / 2) {
        // 定义变量
        var current = this.tail
        var index = this.length - 1

        while (index-- > position) {
            current = current.prev
        }

        current.data = newData
        return true
    }
}

7、removeAt 方法

// 7、封装 removeAt 删除指定位置的节点方法
DoublyLinkedList.prototype.removeAt = function(position) {
    // 越界判断
    if (position < 0 || position > this.length) return null

    var current = this.head
    // 判断是否只有一个节点
    if (this.length == 1) {
        this.head = null
        this.tail = null
    } else {
        if (position == 0) { // 判断删除的是否是第一个节点
            this.head.next.prev = null
            this.head = this.head.next
        } else if (position == this.length - 1) { // 判断删除的是否是最后一个节点
            current = this.tail
            this.tail.prev.next = null
            this.tail = this.tail.prev
        } else { // 删除的是其他的节点

            var index = 0

            while (index++ < position) {
                current = current.next
            }

            current.prev.next = current.next
            current.next.prev = current.prev
        }
    }

    // 长度减一
    this.length -= 1

    return current.data
}

8、remove 方法

// 8、封装 remove 删除指定数据的节点的方法
DoublyLinkedList.prototype.remove = function(data) {
    // 1、根据data获取下标值
    var index = this.indexOf(data)

    // 2、根据index删除对应位置的节点
    return this.removeAt(index)
}

9、isEmpty 方法

// 9、封装 isEmpty 判断链表是否为空方法
DoublyLinkedList.prototype.isEmpty = function() {
    return this.length == 0
}

10、size 方法

// 10、封装 size 返回链表长度方法
DoublyLinkedList.prototype.size = function() {
    return this.length
}

11、getHead 方法

// 11、获取链表的第一个元素
DoublyLinkedList.prototype.getHead = function() {
    return this.head.data
}

12、getTail 方法

// 12、获取链表的最后一个元素
DoublyLinkedList.prototype.getTail = function() {
    return this.tail.data
}

完整代码加测试代码

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

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

    // 封装常见的操作
    // 封装 append 追加方法
    DoublyLinkedList.prototype.append = function(data) {
        // 1、根据data创建节点
        var newNode = new Node(data)

        // 2、判断添加的是否是第一个节点
        if (this.length == 0) {
            this.head = newNode
            this.tail = newNode
        } else {
            newNode.prev = this.tail
            this.tail.next = newNode
            this.tail = newNode
        }

        // 3、length + 1
        this.length += 1
    }

    // 2、封装将链表转成字符串形式的方法
    // 2.1 toString 方法
    DoublyLinkedList.prototype.toString = function() {
        return this.backwordString()
    }

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

        // 依次向前遍历,获取每一个节点
        while (current) {
            resultString += current.data + " "
            current = current.prev
        }

        // 返回最终的值
        return resultString
    }

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

        // 依次向后遍历,获取每一个节点
        while (current) {
            resultString += current.data + " "
            current = current.next
        }

        // 返回最终的值
        return resultString
    }

    // 3、封装 insert 插入方法
    DoublyLinkedList.prototype.insert = function(position, data) {
        // 越界判断
        if (position < 0 || position > this.length) return false

        // 根据 data 创建新的节点
        var newNode = new Node(data)

        // 判断原来的列表是否为空
        if (this.length == 0) {
            this.head = newNode
            this.tail = newNode
        } else {
            if (position == 0) { // 插入到开头
                this.head.prev = newNode
                newNode.next = this.head
                this.head = newNode
            } else if (position == this.length) { // 插入到结尾
                newNode.prev = this.tail
                this.tail.next = newNode
                this.tail = newNode
            } else { // 插入到中间
                var current = this.head
                var index = 0

                while (index++ < position) {
                    current = current.next
                }

                // 修改指针
                newNode.next = current
                newNode.prev = current.prev
                current.prev.next = newNode
                current.prev = newNode
            }
        }

        // length + 1
        this.length += 1

        return true
    }

    // 4、封装 get 获取方法
    DoublyLinkedList.prototype.get = function(position) {
        // 越界判断
        if (position < 0 || position >= this.length) return null

        if (position <= this.length / 2) {
            // 获取对应的data
            var current = this.head // 定义当前值为头节点
            var index = 0 // 定义索引值为0

            while (index++ < position) { // 循环找到需要的 position 对应位置的值
                current = current.next
            }

            return current.data
        } else if (position > this.length / 2) {
            var current = this.tail
            var index = this.length - 1

            while (index-- > position) {
                current = current.prev
            }

            return current.data
        }
    }

    // 5、封装 indexOf 返回指定数据的下标值方法
    DoublyLinkedList.prototype.indexOf = function(data) {
        // 定义变量
        var current = this.head
        var index = 0

        // 查找current的data与传入值的data是否相等
        while (current) {
            if (current.data == data) {
                return index
            }
            index += 1
            current = current.next
        }

        return -1
    }

    // 6、封装 update 更新指定位置数据的方法
    DoublyLinkedList.prototype.update = function(position, newData) {
        // 越界判断
        if (position < 0 || position > this.length) return false

        if (position <= this.length / 2) {
            // 定义变量
            var current = this.head
            var index = 0

            while (index++ < position) {
                current = current.next
            }

            current.data = newData
            return true
        } else if (position > this.length / 2) {
            // 定义变量
            var current = this.tail
            var index = this.length - 1

            while (index-- > position) {
                current = current.prev
            }

            current.data = newData
            return true
        }
    }

    // 7、封装 removeAt 删除指定位置的节点方法
    DoublyLinkedList.prototype.removeAt = function(position) {
        // 越界判断
        if (position < 0 || position > this.length) return null

        var current = this.head
        // 判断是否只有一个节点
        if (this.length == 1) {
            this.head = null
            this.tail = null
        } else {
            if (position == 0) { // 判断删除的是否是第一个节点
                this.head.next.prev = null
                this.head = this.head.next
            } else if (position == this.length - 1) { // 判断删除的是否是最后一个节点
                current = this.tail
                this.tail.prev.next = null
                this.tail = this.tail.prev
            } else { // 删除的是其他的节点

                var index = 0

                while (index++ < position) {
                    current = current.next
                }

                current.prev.next = current.next
                current.next.prev = current.prev
            }
        }

        // 长度减一
        this.length -= 1

        return current.data
    }

    // 8、封装 remove 删除指定数据的节点的方法
    DoublyLinkedList.prototype.remove = function(data) {
        // 1、根据data获取下标值
        var index = this.indexOf(data)

        // 2、根据index删除对应位置的节点
        return this.removeAt(index)
    }

    // 9、封装 isEmpty 判断链表是否为空方法
    DoublyLinkedList.prototype.isEmpty = function() {
        return this.length == 0
    }

    // 10、封装 size 返回链表长度方法
    DoublyLinkedList.prototype.size = function() {
        return this.length
    }

    // 11、获取链表的第一个元素
    DoublyLinkedList.prototype.getHead = function() {
        return this.head.data
    }

    // 12、获取链表的最后一个元素
    DoublyLinkedList.prototype.getTail = function() {
        return this.tail.data
    }



}


// 测试代码
// 1、创建LinkedList
var list = new DoublyLinkedList()

// 2、测试append方法
list.append('abc')
list.append('tng')
list.append('ccc')
console.log(list.toString());
console.log(list.forwordString());
console.log(list.backwordString());

// 测试insert方法
list.insert(0, 'aaa')
list.insert(4, 'ddd')
list.insert(2, 'bbb')
console.log(list.toString());

// 测试get方法
console.log(list.get(0));
console.log(list.get(2));
console.log(list.get(5));
console.log(list.get(1));

// 测试indexOf方法
console.log(list.indexOf('aaa'));
console.log(list.indexOf('bbb'));
console.log(list.indexOf('ddd'));
console.log(list.indexOf('ccc'));

// 测试update方法
list.update(4, 'cc')
console.log(list.toString());

// 测试removeAt方法
list.removeAt(1)
console.log(list.toString());

// 测试remove方法
list.remove('ddd')
console.log(list.toString());

// 测试其他方法
console.log(list.isEmpty());
console.log(list.size());
console.log(list.getHead());
console.log(list.getTail());

下面附上b站视频链接,需要学习的可以去看看(JavaScript算法与数据结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

广和通发布5G智能模组SC151系列,助力AIoT应用更智能高效

2023年5月&#xff0c;广和通发布5G R16智能模组SC151系列。SC151系列基于4nm制程工艺的高通QCM4490解决方案设计&#xff0c;采用8核高性能处理器&#xff0c;为工业与商业物联网终端提供高性能处理能力。面对与日俱增的终端智能化需求&#xff0c;SC151系列将助力打造高生产力…

【致敬未来的攻城狮计划】第2期定向赠书《RT-Thread设备驱动开发指南》+ 《GD32 MCU原理及固件库开发指南》

开启攻城狮的成长之旅&#xff01;这是我参与的由 CSDN博客专家 架构师李肯&#xff08;超链接&#xff1a;http://yyds.recan-li.cn&#xff09;和 瑞萨MCU &#xff08;超链接&#xff1a;瑞萨电子 (Renesas Electronics Corporation)&#xff09; 联合发起的「 致敬未来的攻…

【LeetCode】每日一题:移除链表元素 [C语言实现]

&#x1f47b;内容专栏&#xff1a;《LeetCode刷题专栏》 &#x1f428;本文概括&#xff1a;203.移除链表元素 &#x1f43c;本文作者&#xff1a;花 碟 &#x1f438;发布时间&#xff1a;2023.5.5 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff…

微信小程序的生命周期

微信小程序的生命周期 1.什么是生命周期&#xff1f;2.生命周期的分类3.什么是生命周期函数&#xff1f;4.生命周期函数的分类5.应用生命周期函数6.页面周期函数 1.什么是生命周期&#xff1f; 生命周期&#xff08;Life Cycle&#xff09;是指一个对象从创建 -> 运行 ->…

Ant Design Pro---【02 UMI路由讲解】

在 Umi 中&#xff0c;应用都是单页应用&#xff0c;页面地址的跳转都是在浏览器端完成的&#xff0c;不会重新请求服务端获取 html&#xff0c;html 只在应用初始化时加载一次。所有页面由不同的组件构成&#xff0c;页面的切换其实就是不同组件的切换&#xff0c;你只需要在配…

设计模式——观察者模式(应用广,较难)

是什么&#xff1f; 又被称为发布-订阅模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者同时监听某一个主题对象&#xff0c;这个主题对象在状态变化时&#xff0c;会通知所有的观察者对象&#xff0c;使它们能够更新自己&#xff1b; 结构 抽象主题&…

复兴号列车司机室

复兴号上线有一段时间了&#xff0c;很多旅客都已经体验过&#xff0c;但是&#xff0c;司机室可能绝大多数人都没有机会一睹容颜&#xff0c;今天带你去司机室操纵台去探索一番。 按照上图的圈圈划分为操作台右侧&#xff08;蓝色圈圈&#xff09;、中部&#xff08;红色圈圈…

从C到C++

目录 1、输入输出库与名字空间 2、输入输出流对象cin和cout 3、string字符串类 4、bool类型 5、自增自减--运算符&#xff08;必考&#xff09; 6、其他 1、输入输出库与名字空间 #include <iostream> using namespace std; 每次写代码先把这两个东西加上 <io…

共享内存实现进程间大数据的交换

引言   进程间的数据交换和共享是一种非常重要和实用的技术。大、中型软件的开发设计多是由众多程序设计人员的合作完成&#xff0c;通常一个程序设计人员只负责其中一个或几个模块的开发&#xff0c;这些模块可以是动态链接库也可以是应用程序或是其他形式的程序组件。这些独…

Java 面试必问的线程池原理总结

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;大能老师 | 慕课网讲师 Java 线程池原理总结 &#xff08;一&#xff09;什么是线程池 线程池做的工作主要是控制运行…

Arcgis进阶篇(4)——arcgis js api使用geoserver服务

1、geoserver安装方法 geoserver-2.19.x&#xff08;此版本支持jdk8.x&#xff09;的下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1PkmmjHWWzbUA9fYfa110Ew?pwd8tvf 提取码&#xff1a;8tvf 经测试&#xff0c;最新版本的geoserver需要jdk11&#xff…

elasticsearch 使用的基础工具包及配置安装信息

前言:elasticsearch 工具在查询大量数据上面有明显的优势&#xff0c;但是具体的使用门槛相对较高&#xff0c;具体使用中不同版本使用上还有些差别&#xff0c;需要匹配上 spring boot elasticsearch 版本才能少出错 使用到的工具包&#xff0c;注意具体的版本 elasticsearc…

坚持伙伴优先,共创数据存储新生态

4 月 26 日&#xff0c;2023 阿里云合作伙伴大会上&#xff0c;阿里巴巴集团董事会主席兼 CEO、阿里云智能集团 CEO 张勇表示&#xff0c;阿里云的核心定位是一家云计算产品公司&#xff0c;生态是阿里云的根基。让被集成说到做到的核心&#xff0c;是要坚定走向“产品被集成”…

linux0.12-8-4-sys_call.s

[301页] 8-4 sys_call.s 程序 sys_call.s 程序简单总结&#xff1a; int 0x80 – _system_call int16 – 处理器错误中断 int7 – 设备不存在或协处理器不存在。 int32 – (int 0x20)时钟中断处理程序。 两个系统功能的底层接口&#xff0c;分别是 sys_execve 和 sys_fork 。…

​Prometheus集群编队开发套件升级上市

Prometheus集群编队开发套件是一个面向集群、多智能体相关研究方向的无人机二次开发平台&#xff0c;采用分布式集群算法。与传统无人机集群相比&#xff0c;分布式无人机集群更加灵活、可靠和高效&#xff0c;可应用于更加复杂及多样化的任务场景。 分布式集群科研平台&#x…

Linux 内存管理 pt.2

哈喽大家好我是咸鱼&#xff0c;在《Linux 内存管理 pt.1》中我们学习了什么是物理内存、虚拟内存&#xff0c;了解了内存映射、缺页异常等内容 那么今天我们来接着学习 Linux 内存管理中的多级页表和大页 多级页表&大页 在《Linux 内存管理 pt.1》中我们知道了内核为每…

【Vue】学习笔记-Vue CLI ref props mixin plugin scoped

ref 属性 ref被用来给元素或子组件注册引用信息(id的代替者) 应用在html标签上获取的真实的DOM元素&#xff0c;应用在组件标签上获取的组件实列对象vc使用方式 a. 打标识:或 b.获取&#xff1a;this.$refs.xxx <template><div><h1 v-text"msg" re…

MySQL 优化一MySQL优化步骤

目录 定位执行效率较低的 SQL 1&#xff09;慢查询 2&#xff09;processlist 定位执行效率较低的 SQL 定位执行效率比较慢的 SQL 语句&#xff0c;一般有三种方式 1、可以通过慢查询日志来定位哪些执行效率较低的 SQL 语句。 2、使用 show processlist 命令查看当前 MyS…

紧急下架,AI以假乱真学明星唱歌;哈佛法学院专家谈AI和版权法

几周前&#xff0c;一首据称由 Drake 和 The Weeknd 创作的新歌登陆 TikTok 和 Spotify&#xff0c;并迅速在互联网上像野火一样传播开来。“我袖子上的心”在嘻哈乐迷中获得了好评如潮和高度兴奋&#xff0c;这不仅是因为该曲目具有感染力的歌词和旋律&#xff0c;而且还因为对…

【云原生】云原生服务网格流量控制思考

文章目录 前言一、什么是流量控制&#xff1f;二、存在三种场景三、场景分析 前言 随着云原生技术的不断发展及应用&#xff0c;很多服务都已部署上云。 保障云上环境的稳定是重要的一环。 一个重要的影响稳定的场景就是突发大流量冲击。 面对该场景&#xff0c;较好的应对…