双向链表

目录

单向链表

双向链表

特点

缺点

双向链表的封装


单向链表

只能从头遍历到尾或者从尾遍历到头(一般从头到尾)。也就是链表相连的过程是单向的.
实现的原理是上一个链表中有一个指向下一个的引用

单向链表有一个比较明显的缺点:
我们可以轻松的到达下一个节点,但是回到前一个节点福是很难的.但是,在实际开发中,经常会遇到需要回到上一个节点的情况

双向链表

  • 既可以从头遍历到尾,又可以从尾遍历到头
  • 一个节点既有向前连接的引用,也有一个向后连接的引用.
  • 双向链表可以有效的解决单向链表中提到的问题

特点

  1. 可以使用一个head和一个tail分别指向头部和尾部的节点
  2. 每个节点都由三部分组成: 前一个节点的指针(prev)/保存的元素(item)/后一个节点的指针(next)
  3. 双向链表的第一个节点的prev是null
  4. 双向链表的最后的节点的next是nul 

缺点

每次在插入或删除某个节点时, 需要处理四个引用, 而不是两个.也就是实现起来要困难一些。并且相当于单向链表, 必然占用内存空间更大一些

双向链表的封装

 继承单向链表类 单向链表-CSDN博客

import { LinkedList, Node } from '../04_单向链表/04.链表的封装.js';

// 双向链表的节点类(继承单向链表的节点类)
class DoublyNode extends Node {
    constructor(element) {
        super(element);
        this.prev = null;
    }
}

// 双向链表类(继承单向链表类)
export class DoublyLinkedList extends LinkedList {

    constructor() {
        super();
        this.tail = null;
    }

    // ------------ 链表的常见操作 ------------ //
    // append(element) 往双向链表尾部追加一个新的元素
    // 重写 append()
    append(element) {

        // 1、创建双向链表节点
        const newNode = new DoublyNode(element);

        // 2、追加元素
        if (this.head === null) {
            this.head = newNode;
            this.tail = newNode;
        } else {
            // !!跟单向链表不同,不用通过循环找到最后一个节点
            // 巧妙之处
            this.tail.next = newNode;
            newNode.prev = this.tail;
            this.tail = newNode;
        }

        this.length++;
    }

    // insert(position, data) 插入元素
    // 重写 insert()
    insert(position, element) {
        // 1、position 越界判断
        if (position < 0 || position > this.length) return false;

        // 2、创建新的双向链表节点
        const newNode = new DoublyNode(element);

        // 3、判断多种插入情况
        if (position === 0) { // 在第 0 个位置插入

            if (this.head === null) {
                this.head = newNode;
                this.tail = newNode;
            } else {
                //== 巧妙之处:相处腾出 this.head 空间,留个 newNode 来赋值 ==//
                newNode.next = this.head;
                this.head.perv = newNode;
                this.head = newNode;
            }

        } else if (position === this.length) { // 在最后一个位置插入

            this.tail.next = newNode;
            newNode.prev = this.tail;
            this.tail = newNode;
        } else { // 在 0 ~ this.length 位置中间插入

            let targetIndex = 0;
            let currentNode = this.head;
            let previousNode = null;

            // 找到要插入位置的节点
            while (targetIndex++ < position) {
                previousNode = currentNode;
                currentNode = currentNode.next;
            }

            // 交换节点信息
            previousNode.next = newNode;
            newNode.prev = previousNode;

            newNode.next = currentNode;
            currentNode.prev = newNode;
        }

        this.length++;

        return true;
    }

    // getData() 继承单向链表
    getData(position) {
        return super.get(position);
    }

    // indexOf() 继承单向链表
    indexOf(data) {
        return super.indexOf(data);
    }

    // removeAt() 删除指定位置的节点
    // 重写 removeAt()
    removeAt(position) {
        // 1、position 越界判断
        if (position < 0 || position > this.length - 1) return null;

        // 2、根据不同情况删除元素
        let currentNode = this.head;
        if (position === 0) { // 删除第一个节点的情况

            if (this.length === 1) { // 链表内只有一个节点的情况
                this.head = null;
                this.tail = null;
            } else { // 链表内有多个节点的情况
                this.head = this.head.next;
                this.head.prev = null;
            }

        } else if (position === this.length - 1) { // 删除最后一个节点的情况

            currentNode = this.tail;
            this.tail.prev.next = null;
            this.tail = this.tail.prev;

        } else { // 删除 0 ~ this.length - 1 里面节点的情况

            let targetIndex = 0;
            let previousNode = null;
            while (targetIndex++ < position) {
                previousNode = currentNode;
                currentNode = currentNode.next;
            }

            previousNode.next = currentNode.next; // 删除的前一个的next指向删除的下一个元素
            currentNode.next.perv = previousNode; // 删除的下一个元素的next指向删除的前一个元素

        }

        this.length--;
        return currentNode.data;
    }

    // update(position, data) 修改指定位置的节点
    // 重写 update()
    update(position, data) {
        // 1、删除 position 位置的节点
        const result = this.removeAt(position);

        // 2、在 position 位置插入元素
        this.insert(position, data);
        return result;
    }

    // remove(data) 删除指定 data 所在的节点(继承单向链表)
    remove(data) {
        return super.remove(data);
    }

    // isEmpty() 判断链表是否为空
    isEmpty() {
        return super.isEmpty();
    }

    // size() 获取链表的长度
    size() {
        return super.size();
    }


    // forwardToString() 链表数据从前往后以字符串形式返回
    forwardToString() {
        let currentNode = this.head;
        let result = '';

        // 遍历所有的节点,拼接为字符串,直到节点为 null
        while (currentNode) {
            result += currentNode.data + '--';
            currentNode = currentNode.next;
        }

        return result;
    }

    // backwardString() 链表数据从后往前以字符串形式返回
    backwardString() {
        let currentNode = this.tail;
        let result = '';

        // 遍历所有的节点,拼接为字符串,直到节点为 null
        while (currentNode) {
            result += currentNode.data + '--';
            currentNode = currentNode.prev;
        }

        return result;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker 入门使用说明

Docker 入门使用说明 Docker 安装 Docker 官网&#xff1a;Docker Docker 安装说明&#xff1a;Docker 安装说明 这里由于 Docker 在实时更新&#xff0c;所以每次安装 Docker 用来导入 key 的链接可能会有变化&#xff0c;这里就参考官方的安装方法即可 Docker 常用命令说…

OSCP靶场--Clue

OSCP靶场–Clue 考点(文件读取读取配置中的密码rce认证后利用sudo 提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap -sV -sC -p- 192.168.163.240 --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-14 08:44 EDT Nmap scan report for 192…

网络通信VLAN学习篇

拓扑图 如上图&#xff0c;pc3&#xff0c;pc5同一网络&#xff0c;pc4&#xff0c;pc6同一网络&#xff0c;vlan的划分就是虚拟局域网&#xff0c;局域网的理解就是同一vlan下的设备可以相互通信&#xff0c;不同vlan不可以通信&#xff08;通过三层交换机可以实现通信的&…

缓存穿透、缓存击穿、缓存雪崩及其解决方法

缓存穿透、缓存击穿、缓存雪崩是redis的三大问题。 在介绍这三大问题之前&#xff0c;我们需要先了解Redis作为一个缓存中间件&#xff0c;在项目中是如何工作的。首先看一下在没有缓存中间件的时候的系统数据访问的架构图&#xff1a; 客户端发起一个查询请求的时候&#xff…

面试算法-98-随机链表的复制

题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节…

深入理解 Docker 镜像

1. Docker 镜像的底层原理 1.1 分层的镜像 以我们的pull 命令为例&#xff0c;在下载的过程中我们可以看到docker的镜像好像是一层一层的在下载。 1.2 UnionFS(联合文件系统) 联合文件系统是一种分层、轻量级并且高性能的文件系统&#xff0c;它支持对文件系统的修改作为一次…

马斯克AI大模型Grok开源了!

2024年3月18日&#xff0c;马斯克的AI创企xAI兑现承诺&#xff0c;正式发布了此前备受期待大模型Grok-1。 代码和模型权重已上线GitHub: https://github.com/xai-org/grok-1 截止目前&#xff0c;Grok已经在GitHub上获得了35.2k颗Star&#xff0c;还在不断上升中。 Grok官方博…

202446读书笔记|《夜风颂》——生命的内核是过往和希望 有情在朝暮 长聚长相思

202446读书笔记|《夜风颂》——生命的内核是过往和希望 有情在朝暮 长聚长相思 序现代诗古体诗 《夜风颂》作者王锴&#xff0c;前段时间加入书架的书&#xff0c;前边有几首现代诗挺惊艳&#xff0c;蛮喜欢的&#xff0c;后边古体诗稍逊色些。值得一读的一本小诗集。 序 海鸥之…

蓝桥杯(2):python基础算法【上】

时间复杂度、枚举、模拟、递归、进制转换、前缀和、差分、离散化 1 时间复杂度 重要是看循环&#xff0c;一共运行了几次 1.1 简单代码看循环 #时间复杂度1 n int(input()) for i in range(1,n1):for j in range(0,i):pass ###时间复杂度&#xff1a;123....nn(1n)/2 所以…

Ambari——编译——解决替换yarn 版本后 系mvn 打包找不到yarn 文件问题

您的支持是我继续创作与分享的动力源泉!!! 您的支持是我继续创作与分享的动力源泉!!! 您的支持是我继续创作与分享的动力源泉!!! 报错原因&#xff1a; [ERROR] Failed to execute goal com.github.eirslett:frontend-maven-plugin:1.4:yarn (yarn install) on project amb…

python综合实战案例-数据分析

Python是进行数据分析的好工具&#xff0c;今天就是借助一个案例给大家进行数据分析讲解。 本例设计一个log.txt⽂件&#xff0c;该文件记录了某个项⽬中某个 api 的调⽤情况&#xff0c;采样时间为每分钟⼀次&#xff0c;包括调⽤次数、响应时间等信息&#xff0c;⼤约18万条数…

基于Java中的SSM框架实现快餐店线上点餐系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现快餐店线上点餐系统演示 摘要 随着计算机互联网的高速发展。餐饮业的发展也加入了电子商务团队。各种网上点餐系统纷纷涌现&#xff0c;不仅增加了商户的销售量和营业额&#xff0c;而且为买家提供了极大的方便&#xff0c;足不出户&#xff0c;就能订…

备战蓝桥杯---牛客寒假算法基础集训6

1.并查集数学 分析&#xff1a; 首先我们知道算数基本定理&#xff0c;如果两个数有大于1的质因子&#xff0c;那么我们就需要把他们放在同一个集合&#xff0c;因此我们可以用欧拉刷出1e6范围内的素数&#xff0c;然后依次看输入的数。 拿202*2*5举例子&#xff0c;我们在求…

检索增强生成(RAG)技术:实现流程、作用及应用案例

一. RAG简介 在自然语言处理&#xff08;NLP&#xff09;领域中&#xff0c;检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;技术巧妙地结合了信息检索与神经网络生成模型的力量&#xff0c;通过在生成过程中引入相关的外部信息&#xff0c;实现了在…

【Vue3】组件通信以及各种方式的对比

方式一&#xff1a;props 「父」向「子」组件发送数据 父组件&#xff1a; 定义需要传递给子组件的数据&#xff0c;并使用 v-bind 指令将其绑定到子组件的 props 上。 <template><child-component :message"parentMessage" /> </template><sc…

6. ping在windows中的常见用法

&#xff08;1&#xff09;ping简介 1.ping简介 &#xff08;2&#xff09;在windows上用法 1.直接ping 对方IP&#xff08;无参数时&#xff09; 2.ping -t IP (长ping) 3.ping -n 包数量 4.ping -l 字节大小 IP 5.如何批量的ping一个网段&#xff1f; &#xff08;1&a…

24计算机考研调剂 | 【官方】山东工商学院

山东工商学院 考研调剂招生信息 招生专业&#xff1a; 学院概况&#xff1a; 计算机科学与技术学院始建于1999年&#xff0c;拥有计算机科学与技术一级学科硕士点,在2022软科中国最好学科排名中&#xff0c;计算机科学与技术学科位列全国第104位。在2022年“软科”中国大学专…

【MySQL】2.MySQL数据库的基本操作

目录 数据库基本操作 查看数据库信息 查看数据库结构 显示数据表的结构&#xff08;字段&#xff09; 常用的数据类型 数据库管理操作 SQL语句概述 SQL分类 1.DDL&#xff1a;数据定义语言 1.1创建数据库和表 创建数据库 创建数据表 1.2删除数据库和表 删除数据表…

音视频领域首个,阿里云推出华为鸿蒙 HarmonyOS NEXT 版音视频 SDK

近日&#xff0c;阿里云在官网音视频终端 SDK 栏目发布适配 HarmonyOS NEXT 的操作文档和 SDK&#xff0c;官宣 MediaBox 音视频终端 SDK 全面适配 HarmonyOS NEXT。 此外&#xff0c;阿里云播放器 SDK 也在华为开发者联盟官网鸿蒙生态伙伴 SDK 专区同步上线&#xff0c;面向所…

SpringCloud-记

目录 什么是SpringCloud 什么是微服务 SpringCloud的优缺点 SpringBoot和SpringCloud的区别 RPC 的实现原理 RPC是什么 eureka的自我保护机制 Ribbon feigin优点 Ribbon和Feign的区别 什么是SpringCloud Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发…
最新文章