链表(3):双链表

news2024/11/25 16:20:54

引入

我们之前学的单向链表有什么缺点呢? 

缺点:后一个节点无法看到前一个节点的内容

那我们就多设置一个格子prev用来存放前面一个节点的地址,第一个节点的prev存最后一个节点的地址(一般是null)

这样一个无头双向链表就造好啦😊

老规矩,手搓双链表

初始

和单链表一样,我们也是实现IList接口的方法

初始化差不多,可以去看我前面的博客

数据结构:链表(1)_cx努力编程中的博客-CSDN博客

跟前驱没关系的函数我先放进来

    @Override
    public boolean contains(int key) {
        ListNode cur = this.head;
        while(cur != null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    @Override
    public int size() {
        int count = 0;
        ListNode cur = this.head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
    @Override
    public void display() {
        ListNode cur = head;
        while(cur != null){
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

插入

头插法

实例化一个node,如果只有一个节点的话就把head和last都放在这个节点的下面

node.next = head;

head.prev = node;

head = node;//把head的位置放到node这里

    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if(head == null){
            head = node;
            last = node;
        }else{
            node.next = this.head;
            head.prev = node;
            head = node;
        }
    }

尾插法

跟单链表不一样的是,双链表有last,所以时间复杂度是O(1)

    @Override
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if(head == null){
            head = node;
            last = node;
        }else{
            last.next = node;
            node.prev = head;
            last = node;
        }
    }

指定位置插入

比如我们实例化一个节点,想要将其插入到1和2之间

那么我们就要将cur移动到2的位置(走index步)

这个cur的前驱(节点1)的next就是这个新节点的地址

node.next = cur 

cur.prev.next = node(0x123)

node.prev = cur.prev

cur.prev = node

代码:

    @Override
    public void addIndex(int index, int data) {
        //检查index是否异常
        int len = size();
        if(index < 0 || index > len){
            System.out.println("异常索引"+index);
            return;
        }
        if(index == 0){
            //头插法
            addFirst(data);
            return;
        }
        if(index == len){
            //尾插法
            addLast(data);
            return;
        }
        
        ListNode cur = findIndex(index);
        ListNode node = new ListNode(data);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }

 删除

假如删除中间45这个节点,先让cur走到这个节点,再让45前一个节点的地址等于45后一个节点地址,让45后一个节点的前驱等于45前一个节点的地址

但是假如要删除的是13这个节点(头节点),那么cur.prev.next = cur.next这行代码有问题

所以我们这么干,直接把头节点的后一个节点的前驱prev置为null就行了

                if(cur == head){
                    head = head.next;
                    head.prev = null;

假如要删除的是56这个节点(尾节点),那么cur.next.prev = cur.prev这行代码有问题

那我们这么干,直接把尾节点的前一个节点的next置为null就行了

cur.prev.next = cur.next;

last = last.prev;

我们发现尾节点的删除和中间节点删除有一行代码是一样的

cur.prev.next = cur.next;

我们可以把它作为这二者的公用代码

                    cur.prev.next = cur.next;
                    if(cur.next == null){
                        last = last.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }

其实这段代码还存在问题

当链表只有一个元素的时候,

 

head = head.next //这行代码一走完表明head此时已经是空的了,下一行代码还说head.prev就根本不存在(注意:head.prev = null是赋值操作,首先你得有head.prev才能进行赋值)

更改代码:

整个remove代码

    @Override
    public void remove(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key){
                if(cur == head){
                    head = head.next;
                    if(head == null){
                        last = null;
                    }else{
                        head.prev = null;
                    }
                }else {
                    cur.prev.next = cur.next;
                    if(cur.next == null){
                        last = last.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }else{
                cur = cur.next;
            }
        }
    }

删除指定值的所有相同值的节点

跟上面的代码很像,区别就是删除之后不要return,让cur继续往下走

    @Override
    public void removeAllKey(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key) {
                if (cur == head) {
                    head = head.next;
                    if (head == null) {
                        last = null;
                    } else {
                        head.prev = null;
                    }
                } else {
                    cur.prev.next = cur.next;
                    if (cur.next == null) {
                        last = last.prev;
                    } else {
                        cur.next.prev = cur.prev;
                    }
                }
            }
            cur = cur.next;
        }
    }

清除链表

暴力方法:

head = null;  last = null;

细致方法:

定义一个cur,遍历整个链表,每到一个节点就把prev和next都置为空

    @Override
    public void clear() {
        ListNode cur = head;
        while(cur!=null){
            ListNode curNext = cur.next;//防止将这个节点的next置为空之后,cur没办法继续遍历
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
    }

总结:

ArrayList和LinkedList的区别是?

如果是经常根据下标查找的,使用顺序表(ArrayList)或者数组

如果经常进行插入和删除操作的,就使用链表(LinkedList)

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

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

相关文章

Dart-C、Dart-Kotlin/Java/Swift/Object-C、Kotlin-C数据类型对照表

参考一&#xff1a;Dart FFI 数据类型映射 一、Dart—C 的数据类型对照 二、Dart—Java/Kotlin/Object-C/Swift 的数据类型对照 三、C—Kotlin 的数据类型对照

WordPress发布文章上传图片如何默认选择全尺寸

最近发现每次上传图片插入到文章中&#xff0c;图片默认选择缩略图大小并靠左&#xff0c;每次都需要手动修改一下图片信息&#xff0c;比较麻烦。 今天为大家提供修改默认上传图片大小和对齐方式等。 将代码添加到functions.php&#xff1a; //WordPress 设置图片的默认显示…

Qt 样式表大全整理

【QT】史上最全最详细的QSS样式表用法及用例说明_qt样式表使用大全_半醒半醉日复日&#xff0c;花落花开年复年的博客-CSDN博客 QT样式表的使用_qt 设置按下 release hover 按钮样式表_create_right的博客-CSDN博客 QPushButton {border-image: url(:/Start_Stop.png); } QPu…

H5营销观察:场景应用的重要性

H5营销是很常见的营销方式&#xff0c;通常用于各种营销活动和推广活动。对于品牌部或市场部人员&#xff0c;提到“场景化营销”&#xff0c;很多人的第一反应就是如何制作一个有创意、能快速传播的H5。 似乎H5营销已经成为市场人专业度的考量&#xff0c;不论是品牌宣传、活动…

001flutter基础学习

flutter基础学习 参考:https://book.flutterchina.club/chapter1/flutter_intro.html Flutter是谷歌的移动UI框架跨平台: Linux,Android, IOS,Fuchsia原生用户界面:它是原生的,让我们体验更好,性能更好开源免费&#xff1a;完全开源,可以进行商用Flutter与主流框架的对比 Cor…

Rt-Thread 移植4--对象容器实现(KF32)

1.对象 1,1 什么是对象 所有的数据结构都是对象 1.2代码实现 1.2.1 对象类型枚举定义 rtdef.h 添加rt_object_class_type enum rt_object_class_type{RT_Object_Class_NULL0,RT_Object_Class_Thread ,RT_Object_Class_Semaphore,RT_Object_Class_Mutex,RT_Object_Class_E…

【java学习】JavaBean(28)

文章目录 1. 概念 1. 概念 JavaBean 是一种 Java 语言写成的可重用组件。 所谓 javaBean &#xff0c;是指符合如下标准的 Java 类&#xff1a; (1) 类是公共的 (2) 有一个无参的公共的构造器 (3) 有属性&#xff0c;属性一般是私有的&#xff0c;且有对应的 get 、set 方法 …

cad问题:无法识别的版本,不能读取

cad安装目录或文件名、新建时的文件模板&#xff0c;含有“无”这个字就会出现此问题&#xff0c;只要把 无这个字改成没&#xff0c;即可解决

Kafka进阶

Kafka进阶 Kafka事务 kafka的事务机制是指kafka支持跨多个主题和分区的原子性写入&#xff0c;即在一个事务中发送的所有消息要么全部成功&#xff0c;要么全部失败。 kafka的事务机制涉及到以下几个方面&#xff1a; 事务生产者&#xff08;transactional producer&#x…

【牛客面试必刷TOP101】Day12.BM72 连续子数组的最大和和BM80 买卖股票的最好时机(一)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;牛客面试必刷TOP101 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&…

StatefulSet 简单实践 Kubernetes

概述 在Kubernetes集群中部署MySQL和Mongodb的StatefulSet服务。 MySQL有官方文档的指引 其他网站博客的指引实现 Mongodb修改operator的Deployment进行简单的实现 MySQL-StatefulSet 参考官方文档&#xff1a;运行一个有状态的应用程序 | Kubernetes 深入剖析Kubernete…

matlab第三方硬件支持包下载和安装

1、在使用matlab内部的附加功能安装时&#xff0c;由于matlab会验证是否正版无法打开 2、在matlab官网直接找到对应的硬件支持包下载&#xff0c;但是是下图的安装程序 可以直接在matlab中跳转到该程序所在的文件夹双击安装&#xff0c;但是安装到最后出错了 3.根据出错时mala…

创邻科技Galaxybase—激活数据要素的核心引擎

10月11日下午&#xff0c;创邻科技创始人张晨博士受杭州电子科技大学邀请&#xff0c;前往杭电校园开展交流分享。交流会中&#xff0c;张晨博士为现场的师生带来一场题为《图数据库——激活数据要素的新基建》的精彩分享&#xff0c;探讨数字经济时代底层技术的创新价值与图技…

CRM系统管理多渠道客户的方法

很多企业同时拥有多个销售渠道&#xff0c;由于客户来自不同的销售渠道&#xff0c;数据非常分散&#xff0c;管理起来费时费力。或许您可以使用CRM客户管理系统来管理不同渠道的客户&#xff0c;下面说说企业常见的销售渠道有哪些&#xff1f;CRM系统如何管理多渠道客户&#…

一招搞定,终止端口号进程

场景&#xff1a;聪明的小明写了个到点执行关机的脚本&#xff0c;再次启动项目时发现端口号占用&#xff0c;操作步骤来了。 步骤1&#xff1a; winr&#xff0c;cmd走起 netstat -ano | findstr "端口号" 步骤2&#xff1a; 查询该进程 tasklist | findstr "P…

苹果macOS Sonoma 14正式版 “黑苹果”且用且珍惜

9月下旬&#xff0c;苹果正式发布了全新的桌面操作系统macOS Sonoma 14&#xff0c;对于一众的“黑苹果”用户来说&#xff0c;这是个让人既兴奋又害怕的消息&#xff0c;兴奋的是又有新系统可以升级了&#xff0c;害怕的是“黑苹果”距离寿终正寝的时间也越来越近了。 所谓的“…

软件测试面试基础篇(含答案)

1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前 3 年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自己&a…

【QT】Ubuntu 交叉编译安装 QT 5.12.7 源码

目录 1、下载 QT 源码包 2、搭建安装环境(下载依赖库) 3、创建QT源码编译脚本 4、运行编译脚本 1、下载 QT 源码包 QT5.12.7源码下载地址: download | QT 5.12.7 选择任意一种下载即可&#xff0c;适用于 Windows 和 Linux 环境 这里选择的是.tar.xz 类型&#xff0c;上…

CustomNavBar 自定义导航栏视图

1. 创建偏好设置键 CustomNavBarTitlePreferenceKey.swift import Foundation import SwiftUI//State private var showBackButton: Bool true //State private var title: String "Title" //"" //State private var subtitle: String? "SubTitl…

树莓派部署.net core网站程序

1、发布你的项目 使用mobaxterm上传程序 回到mobaxterm,f进入目录输入&#xff1a; cd webpublish 运行程序&#xff1a;dotnet WebApplication1.dll 访问地址为&#xff1a;http://localhost:5000,尝访问如下&#xff1a; 已经出现 返回的json&#xff0c;证明是可以访问的…