当前位置: 首页 > news >正文

【数据结构】双向链表的模拟实现(无头)

目录

前言:

1、认识双向链表中的结点 

 2、认识并创建无头双向链表

3、实现双向链表当中的一些方法

3.1、遍历输出方法(display)

3.2、得到链表的长度(size)

3.3、查找关键字key是否包含在双链表中(contains)

3.4、头插法(addFirst)

【代码思路】

 【代码实现】

3.5、尾插法 (addIndex)

【代码思路】

 【代码实现】

3.6、任意位置插入 ,第一个数据结点为0号下标(addIndex)

【代码思路】

 【代码示例】

3.7、 删除第一次出现关键字key的结点(remove)

【代码思路】

第一种情况,删除头节点。 

 【代码示例】

 3.8、删除所有值为key的结点(removeAllKey)

【代码思路】

【代码示例】 

3.9、清空双向链表(clear)

 【代码思路】

 【代码示例】


前言:

单向链表能够解决逻辑关系为"一对一"数据的存储问题,但是在解决某些特殊问题的时候,单链表并不是效率最有的存储结构。比如,需要找某个节点的前驱节点,使用单链表并不合适,单链表更适合"从前往后"找,而"从后往前"找并不是单链表的强项。这里就要使用双向链表来解决这类问题。


1、认识双向链表中的结点 

双向链表中的结点有两个指针域和一个数据域,一个指针指向前驱结点,一个指向后继节点。(双向链表当中第一个结点的prev为null,最后一个结点的next为null)

 2、认识并创建无头双向链表

在Java当中,双链表相比于单链表增加了一个引用last,last永远指向双链表的最后一个结点。

 创建链表类

public class MyLinkedList {static class ListNode{//结点类public int val;public ListNode prev;//前驱public ListNode next;//后继public ListNode(int val){this.val = val;}}public ListNode head;public ListNode last;//指向双向链表的结尾
}

3、实现双向链表当中的一些方法

以下这些方法写在MyLinkdeList类当中

3.1、遍历输出方法(display)

    public void display(){ListNode cur = head;while(cur != null){//说明cur还没有遍历完这个链表System.out.print(cur.val+" ");cur = cur.next;}System.out.println();//当整体输出完成之后换行,下一次打印的时候在下一行}

3.2、得到链表的长度(size)

    public int size(){ListNode cur = head;int len = 0;while(cur != null){len++;//因为cur是从head向后遍历,先通过len++将head计算在内cur = cur.next;}return len;}

3.3、查找关键字key是否包含在双链表中(contains)

    public Boolean contains(int key){ListNode cur = head;while(cur != null){if(cur.val == key){//如果cur在遍历的过程中找到了return true;}cur = cur.next;//没有找到就向后走}return false;//遍历完还没找到返回false}

3.4、头插法(addFirst)

【代码思路】

头插法存在两种情况

 【代码实现】

    public void addFirst(int data){ListNode node = new ListNode(data);//创建一个新的结点if(head == null){//如果链表为空,插入结点之后,头和尾都指向nodehead = node;last = node;}else{//如果链表不为空。先连接后继,再链接前驱,最后将head前移node.next = head;head.prev = node;head = node;}}

3.5、尾插法 (addIndex)

【代码思路】

尾插法存在两种情况。

 【代码实现】

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

❗❗❗ 总结:

单链表的时间复杂度为O(N),而双链表的时间复杂度为O(1)。


3.6、任意位置插入 ,第一个数据结点为0号下标(addIndex)

【代码思路】

 【代码示例】

    public void addIndex(int index,int data){if(index < 0 || index >size()){//检查位置的合法性return;//这里可以抛异常,也可以直接return}if(index == 0){//在链表的开头插入结点addFirst(data);return;}if(index == size()){//再链表的结尾插入结点addLast(data);return;}ListNode node = new ListNode(data);//创建一个新的结点ListNode cur = findIndex(index);//通过调用这个方法,找到要插入的位置node.next = cur;cur.prev.next = node;node.prev = cur.prev;cur.prev = node;}//通过这个方法来找要插入的位置private ListNode findIndex(int index){ListNode cur = head;while(index != 0){//从头开始遍历链表。cur = cur.next;index--;}return cur;}

3.7、 删除第一次出现关键字key的结点(remove)

【代码思路】

第一种情况,删除头节点。 

 第二种和第三种情况,删除中间节点和结尾

 

 【代码示例】

    public void remove(int key){ListNode cur = head;while(cur != null){//开始删除了if(cur.val == key){//1、删除的是头节点if(cur == head){head = head.next;//head向后移//处理链表只有一个结点的情况if(head != null) {head.prev = null;//将head的前驱置为空}}else{//删除的是中间和结尾cur.prev.next = cur.next;//2、删除中间结点if(cur.next != null){cur.next.prev = cur.next;//3、删除尾巴结点}else{last = cur.prev;}}return;//这个return对应的是第2个if,找到一个与key值相等的结点,删除之后,就返回,只删一个与key值相等的结点}cur = cur.next;//对应最开始的if,若是要和删除的key不相同,继续向后走}}


 3.8、删除所有值为key的结点(removeAllKey)

【代码思路】

当写出删除一个值为key的结点的代码,那么删除所有值为key的结点的代码,就非常简单了,只需要将上述代码中的return去掉就可以了。让上述的代码从头跑到结尾就行,这样cur在遍历链表的时候,也只是遍历了一遍,就将所有与key值相等的结点就删除完了。他的时间复杂度为O(N).

【代码示例】 

    public void removeAllKey(int key){ListNode cur = head;while(cur != null){//开始删除了if(cur.val == key){//1、删除的是头节点if(cur == head){head = head.next;//head向后移//处理链表只有一个结点的情况if(head != null) {head.prev = null;//将head的前驱置为空}}else{//删除的是中间和结尾cur.prev.next = cur.next;//2、删除中间结点if(cur.next != null){cur.next.prev = cur.next;//3、删除尾巴结点}else{last = cur.prev;}}}cur = cur.next;}}

3.9、清空双向链表(clear)

这里很多人会想到将head和last直接置为空,不让head引用和last引用,引用链表的节点即可,但是head所引用的结点的后继结点,还引用这个结点,last所引用的结点的前驱结点,还引用这个结点。所以这样的操作还是不能将链表清空,必须要将双向链表的所有结点的指针域清空。

 【代码思路】

 【代码示例】

    public void clear(){ListNode cur = head;while(cur != null){//将每个结点的指针域都置为空,由于这里的数据域是基本数据类型,不用置空,但是当数据域当中为引用数据类型的时候,数据域还要置空ListNode curNext = cur.next;cur.prev = null;cur.next = null;cur = curNext;}head = null;//因为head和last作为引用,还在引用链表的第一个结点和最后一个结点。last = null;}

相关文章:

【数据结构】双向链表的模拟实现(无头)

目录 前言&#xff1a; 1、认识双向链表中的结点 2、认识并创建无头双向链表 3、实现双向链表当中的一些方法 3.1、遍历输出方法&#xff08;display&#xff09; 3.2、得到链表的长度&#xff08;size&#xff09; 3.3、查找关键字key是否包含在双链表中(contains) 3.…...

vue自定义指令---处理加载图片失败时出现的碎图,onerror事件

目录 一、自定义指令 1、局部注册和使用 2、全局注册和使用 二、自定义指令处理图片加载失败&#xff08;碎图&#xff09; 一、自定义指令 vue中除v-model、v-show等内置指令之外&#xff0c;还允许注册自定义指令&#xff0c;获取DOM元素&#xff0c;扩展额外的功能。 1、局…...

加盟管理系统挑选法则,看完不怕被坑!

经营服装连锁店铺究竟有多难&#xff1f;小编已经不止一次听到身边的老板&#xff0c;抱怨加盟连锁店铺难以管理了&#xff0c;但同时呢&#xff0c;也听到了很多作为加盟商的老板&#xff0c;抱怨总部给的支持和管理不到位。服装加盟店铺管理&#xff0c;到底有哪些难点呢&…...

alertmanager笔记

1 prometheus的思想 所有告警都应该立刻处理掉&#xff0c;不应该存在长时间未解决的告警。所以具体的表现就是高频的数据采集&#xff0c;和告警的自动恢复&#xff08;默认5分钟&#xff09; 2 alertmanager API调用 使用如下命令即可手工制造告警&#xff0c;注意startsA…...

Android Jetpack组件之WorkManager后台任务管理的介绍与使用(二)

一、介绍 通过上一篇文&#xff0c;Android Jetpack组件之WorkManager后台任务管理的介绍与使用(一)_蜗牛、Z的博客-CSDN博客 我们可以弄清楚workmanager从接入到使用的基本流程。基本可以满足我们日常。那只是简单的入门。如果遇到更复杂的功能&#xff0c;那简单的就无法满…...

【MySQL】第十七部分 约束

【MySQL】第十七部分 约束 文章目录【MySQL】第十七部分 约束17. 约束17.1 约束的分类17.2 非空约束17.3 唯一性约束17.4 主键约束17.5 自增列约束17.6 外键约束17.7 默认约束17.8 check约束总结17. 约束 约束: 可以在创建表的时候规定约束,也可以在表创建之后添加,约束顾名思…...

java ssm集装箱码头TOS系统调度模块的设计与实现

由于历史和经济体制的原因&#xff0c;国内码头物流企业依然保持大而全的经营模式。企业自己建码头、场地、经营集装箱运输车辆。不过近几年来随着经济改革的进一步深入和竞争的激烈&#xff0c;一些大型的码头物流企业逐步打破以前的经营模式&#xff0c;其中最明显的特征就是…...

MS14-064(OLE远程代码执行漏洞复现)

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;内网安全-漏洞复现 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xf…...

【C++深陷】之shared_ptr

0. 什么是智能指针 使用new 和delete 手动进行动态内存管理很容易出现内存泄漏等问题。C11为了更安全、更方便的管理动态内存&#xff0c;新的标准库提供了两种智能指针&#xff08;smart pointer&#xff09;&#xff1a;shared_ptr和unique_ptr&#xff0c;以及一个伴随类we…...

SpringMVC中遇到的错误

SpringMVC中遇到的错误1.web.xml中配置SpringMVC核心类: DispatcherServlet 报错解决方案&#xff1a;添加Tomcat包2. not declaration can be found for element--------‘mvc:annotation-driven‘通配符的匹配很全面, 但无法找到元素 mvc:annotation-driven 的声明解决方案&a…...

姿态估计端到端新方案 | DirectMHP:用于全范围角度2D多人头部姿势估计

前言 现有的头部姿势估计主要集中在具有预先检测到的正面头部的单个人&#xff0c;这依赖于单独训练的面部检测器&#xff0c;不能很好地泛化到完整的视点。在本文中&#xff0c;作者关注全范围 MPHPE 问题&#xff0c;并提出了一个名为 DirectMHP 的直接端到端简单基线&#x…...

jvm学习的核心(五)---垃圾回收算法和常见垃圾回收器

文章目录1.垃圾回收算法**1.1. 标记阶段****1.2. 清除阶段**1.2.1.标记清除算法1.2.2.标记复制算法1.2.3.标记整理算法1.3.引用2.常见的垃圾回收器2.1.Serial回收器2.2.ParNew回收器2.3.Parallel回收器2.4.CMS回收器<font color red>2.5.G1垃圾回收器ZGC回收器&#xff…...

亿级高并发电商项目-- 实战篇 --万达商城项目 二(Zookeeper、Docker、Dubbo-Admin等搭建工作

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…...

【C#基础】 C# 数据类型总结

序号系列文章0【C#基础】初识编程语言C#1【C#基础】C# 程序通用结构总结2【C#基础】C# 程序基础语法解析文章目录前言数据类型一. 值类型&#xff08;Value types&#xff09;二. 引用类型&#xff08;Reference types&#xff09;三. 指针类型&#xff08;Pointer types&#…...

格子玻尔兹曼法介绍

1 LBM简介格子玻尔兹曼法&#xff08;Lattice Boltzmann Method&#xff09;简称LBM&#xff0c;是一种CFD算法&#xff0c;可求解流动、传热等常见CFD问题。LBM基于格子玻尔兹曼方程&#xff08;LBE&#xff09;&#xff0c;从介观尺度&#xff08;mesoscope&#xff09;描述了…...

活动星投票在时间的河流上造园分组怎么设置如何进行分组报名

“在时间的河流上造园”网络评选投票_免费小程序运行系统_企业有关的投票_微信投票的应用小程序投票活动如何做&#xff1f;很多企业在运营当中&#xff0c;都会通过投票活动来进行推广&#xff0c;从而达到吸粉、增加用户粘度等效果。而此类投票活动&#xff0c;通过小程序就可…...

c#小笔记本-基础

c#基本知识一.基础操作1.打印-writeline,write2.输入-readline,readkey二.变量1.折叠代码-#region&#xff0c;#endregion2.变量类型&#xff08;在c语言变量类型上新增的&#xff09;三.常量-const四.转义字符五.显示转换1.括号强转-低精度装高精度2.parse法-作用于字符串3.co…...

DamiCMS SQL注入分析

2023年将会持续于B站、CSDN等各大平台更新&#xff0c;可加入粉丝群与博主交流:838681355&#xff0c;为了老板大G共同努力。 一、入口文件(单入口文件模式) 看一下Index.php文件代码&#xff1a;引入了php_safe.php文件 查看一下php_safe.php防御文件&#xff1a; 对变量e…...

图傅里叶变换的推导和理解

把传统的傅里叶变换以及卷积迁移到Graph上来,核心工作其实就是把拉普拉斯算子的特征函数 e − i ω t e^{-i\omega t} e−iω...

Java八股文(Java面试题)

JDK、JRE、JVM 三者之间的关系&#xff1f;JDK&#xff08;Java Development Kit&#xff09;&#xff1a;是Java开发工具包&#xff0c;是整个Java的核心&#xff0c;包括了Java运行环境JRE、Java工具和Java基础类库。它能够创建和编译程序。JRE&#xff08;Java Runtime Envi…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...