模拟实现链表的功能

1.什么是链表?

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。

 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  1. 单向或者双向                
  2. 带头或者不带头                      
  3. 循环或者非循环                                         

无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如 哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。  

无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

2.如何定义一个链表?

无头单向链表的每一个节点有两个属性,分别是值val和存储下一个节点的地址。

我们来回顾内部类的定义:当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服 务,那么这个内部的完整结构最好使用内部类

这里我们把每个节点定义为内部类正合适

    static class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;//定义头节点

此时的head: 

注:这里的next中存储的下一个节点的位置,所以类型是ListNode;

使用构造方法初始化的时候,仅仅初始化val的值,next节点默认为null

3.链表的模拟实现

这里我们模拟实现的是无头单向非循环链表 

// 1、无头单向非循环链表实现public class SingleLinkedList {//头插法public void addFirst(int data){}//尾插法public void addLast(int data){}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){}//查找是否包含关键字key是否在单链表当中public boolean contains(int key){return false;}//删除第一次出现关键字为key的节点public void remove(int key){}//删除所有值为key的节点public void removeAllKey(int key){}//得到单链表的长度public int size(){return -1;}public void clear() {}public void display() {}}

 (1)头插addFirst的实现

解析:

假设有这样一组链表,我们想把node节点添加到该链表的头节点

实现效果:

  1. 创建需要头插的node节点
  2. 我们需要把node节点的next指向下一个结点的地址
  3. 把node节点变为头节点即可
public void addFirst(int val) {ListNode node = new ListNode(val);node.next = head;head = node;}

(2)尾插addLast的实现

解析: 

实现效果:

思路:

  1. 创建需要尾插的node节点
  2. 通过循环找到链表的最后一个节点,将原本链表最后节点的next的null变为所要尾插节点的地址

操作:

  1. 空链表怎么处理?
    首先我们判断链表是否是空链表,如果是,直接将node节点变为头节点即可
  2. 怎么找到链表的最后一个节点?
    通过while循环遍历来找到链表的最后一个节点,如果没找到就一直循环移动到下一个节点,如果找到了,我们将node的地址赋值给最后一个节点的next
  3. 为什么要定义cur节点来遍历?
    我们定义一个cur节点来存储head节点,以防遍历后数据丢失
  4. 循环的条件?
    while循环的条件,判断每个节点的next是否为空,为空说明找到了最后一个节点,也就是cur.next != null
public void addLast(int val) {ListNode cur = head;ListNode node = new ListNode(val);if(head == null) {head = node;return;}while (cur.next != null) {cur = cur.next;}cur.next = node;}

 (3)addIndex()的实现

此方法实现在任意位置插入节点 

实现效果:
思路:

  1. 找到所要插入位置的前一个节点
  2. 将node的next指向cur的next节点;将前一个结点cur的next指向node的地址

操作:

  1. 如何处理传入的下标不合法问题?
    这里我们定义一个异常处理,当下标小于0,或者大于链表长度时,抛出异常,并用try  catch()语句来进行异常的捕获
  2. 插入的位置在头部和尾部怎么处理?
    对应头插法和尾插法,直接调用写好的方法即可
  3. 如何找到前一个结点?
    我们可以专门写一个方法来实现此操作,定义一个count变量来表示循环次数,只需循环到index-1的位置即可
  4. 如何进行连接?
    将node的next指向cur的next节点;将前一个结点cur的next指向node的地址。需要注意的是需要先将添加的节点与后面的节点进行绑定,再对前面的进行绑定
public void addIndex(int index, int val) {//1.判断合法性try {checkIndex(index);}catch (IndexNotLegalException e) {e.printStackTrace();}//2.index == 0  || index == size()if(index == 0) {addFirst(val);return;}if(index == size()) {addLast(val);return;}//找到前一个结点ListNode cur = findIndexSubOne(index);//4. 进行连接ListNode node = new ListNode(val);node.next = cur.next;cur.next = node;}private ListNode findIndexSubOne(int index) {ListNode cur = head;int count = 0;while (count != index-1) {cur = cur.next;count++;}return cur;}private void checkIndex(int index) throws IndexNotLegalException{if(index < 0 || index > size()) {throw new IndexNotLegalException("index不合法");}}

IndexNotLegalException.java 文件

public class IndexNotLegalException extends RuntimeException {public IndexNotLegalException() {}public IndexNotLegalException(String message) {super(message);}
}

(4)contains()的实现

此方法判断链表中是否包含val值,包含则返回true,否则返回false

思路:

  1. 遍历链表,与每个节点的val值进行对比

操作:

  1. 循环的条件?
    因为需要将每个节点的val值进行比较,所以条件为cur != null;
  2. 如果找到就返回true,如果没有就继续遍历,遍历完好没找到就返回false;
    public boolean contains(int val) {ListNode cur = head;while (cur != null) {if (cur.val == val) {return true;}cur = cur.next;}return false;}

(5)remove()的实现

此方法是删除第一次出现关键字为val的节点

实现效果:

思路:

  1. 找到所要删除节点的前一个节点,将该节点与所要删除节点的后一个节点进行拼接来完成删除操作

操作:

  1. 如何找到所要删除节点的前一个节点?
    当cur.next.val == 34(所要删除的节点)时,定义一个del节点,cur的下一个节点就是del,ListNode del = cur.next;
    为什么不能用cur.val而用cur.next.val来对比判断所要删除的节点呢?
    因为此时是一个单向的链表当我们的cur走到要删除的节点是就不能修改前面的节点了,这样就无法实现改变前一个结点的val值了
  2. 如何进行删除?
    我们定义所要删除的节点为del,将cur的next指向del的next,即:cur.next = del.next;(cur.next = cur.next.next)
  3. 循环条件是什么?
    判断每个节点的val是否是所要删除的val,循环条件是cur.next != null,通过cur.next.val就可以访问到下一个节点的val值。
    为什么不能通过cur != null来作为循环条件呢?
    在我们寻找删除结点的前一个节点时,我们是通过cur.next.val == 34(所要删除的节点)进行值的对比来寻找,如果我们通过cur != null来判断,当cur走到最后一个节点时,next为null,此时cur.next.val就会出现空指针异常
  4. 为什么需要特殊判断头节点?
    因为我们是通过cur.next.val来开始判断的,并没有判断头节点的val值
    所以我们需要进行特殊判断, head = head.next;
public void remove(int val) {if(head == null) {return;}if(head.val == val) {head = head.next;return;}ListNode cur = head;while (cur.next != null) {if (cur.next.val == val) {ListNode del = cur.next;cur.next = del.next;return;}cur = cur.next;}}

(6)removeAllKey()的实现

此方法是删除所有出现关键字为val的节点

操作:

  1. cur代表当前需要删除的节点

    prev代表当前需要删除节点cur的前驱节点

  2. 如果找到需要删除的节点,prev.next = cur.next;cur = cur.next;

  3. 如果没找到所要删除的节点,移动prev节点prev = cur;再移动cur判断下一个节点cur = cur.next;

  4. 最后的效果

  5. 如何处理头节点就是要删除的节点的情况?

    先将头节点以外的删除再来考虑头节点位置即可
    if(head.val == val) {
                head = head.next;
            }

    也可先考虑头节点的情况,while循环判断

public void removeAllKey(int val) {//1. 判空if (head == null) {head = head.next;}//2. 定义prev 和 curListNode prev = head;ListNode cur = head.next;//3.开始判断并且删除while (cur != null) {if (cur.val == val) {//找到了prev.next = cur.next;} else {prev = cur;}cur = cur.next;}//4.处理头节点if(head.val == val) {head = head.next;}}

(7)size()的实现

遍历链表,用count计数即可

public int size() {int count = 0;ListNode cur = head;while (cur != null) {count++;cur = cur.next;}return count;}

(8)clear()的实现

将每一个节点的next置空即可,注意这里需要单独把头节点置空

public void clear() {ListNode cur = head;while (cur != null) {ListNode curN = cur.next;//cur.val = null;cur.next = null;cur = curN;}head = null;}

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

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

相关文章

车载测试到底怎么样?真实揭秘!

什么是车载智能系统测试&#xff1f; 车载智能系统&#xff0c;是汽车智能化重要的组成部分&#xff0c;由旧有的车载资通讯系统结合联网汽车技术所演进而来&#xff0c;随着软硬件技术的不断进步&#xff0c; 让车载智能系统拥有强大的运算能力及多元化的应用功能。 车载智能…

苹果iPad M4:Console级别图形和AI强大功能

苹果iPad M4&#xff1a;Console级别图形和AI强大功能 Apple近日发布了最新的M4芯片&#xff0c;旨在为iPad Pro系列带来明显的性能提升和电池续航时间延长。在本篇报道中&#xff0c;我们将详细介绍M4芯片的特点、性能改进和为创意专业人士带来的影响。 M4芯片的强大功能 …

图解项目管理必备十大管理模型及具体应用建议

心智模型是根深蒂固存在于人们心中&#xff0c;影响人们如何理解这个世界&#xff08;包括我们自己、他人、组织和整个世界&#xff09;&#xff0c;以及如何采取行动的诸多假设、成见、逻辑、规则&#xff0c;甚至图像、印象等。本图通过对心智模型的分类和描述&#xff0c;表…

【Linux】shell基础,shell脚本

Shell Shell是一个用C语言编写的程序&#xff0c;接受用户输入的命令&#xff0c;并将其传递给操作系统内核执行。Shell还负责解释和执行命令、管理文件系统、控制进程&#xff0c;是用户使用Linux的桥梁。Shell既是一种命令语言&#xff0c;又是一种程序设计语言 Shell脚本 Sh…

上位机图像处理和嵌入式模块部署(树莓派4b和c++新版本的问题)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 自己读书的时候是03年&#xff0c;学习c也是差不多04年开始&#xff0c;到现在基本上20年了。这20年过程当中&#xff0c;其实c的语言版本一直是在…

【stomp 实战】spring websocket 接收消息源码分析

后台消息的发送过程&#xff0c;我们通过spring websocket用户消息发送源码分析已经了解了。我们再来分析一下后端接收消息的过程。这个过程和后端发送消息过程有点类似。 前端发送消息 前端发送消息给服务端的示例如下&#xff1a; 发送给目的/app/echo一个消息。 //主动发…

科林算法_3 图

一、图论基础 多对多的关系 定义&#xff1a;G(V,E) Vertex顶点 Edge边 顶点的集合V{v1,v2} 边的结合E{(v1,v2)} 无向图(1,2) 有向图<1,2> 依附&#xff1a;边(v1,v2)依附于顶点v1,v2 路径&#xff1a;&#xff08;v1,v2)(v2,v3) 无权路径最短&#xff1a;边最少…

程序员不会告诉老板的那些神器

目录 1. 持续集成工具&#xff1a;CruiseControl&#xff08;简称CC&#xff09; 2. 代码风格、质量检查工具&#xff1a;StyleCop 3.AI工具 3.1 AI助力编写开发日报 3.2 AI助力编写普适性代码 3.3 AI助力生成代码注释 3.4 AI助力重构代码去掉“坏味道” 3.5 AI助力…

【小白的大模型之路】基础篇:Transformer细节

基础篇&#xff1a;Transformer 引言模型基础架构原论文架构图EmbeddingPostional EncodingMulti-Head AttentionLayerNormEncoderDecoder其他 引言 此文作者本身对transformer有一些基础的了解,此处主要用于记录一些关于transformer模型的细节部分用于进一步理解其具体的实现机…

渗透之sql注入---宽字节注入

目录 宽字节注入原理&#xff1a; 实战&#xff1a; 源码分析&#xff1a; 开始注入&#xff1a; 找注入点&#xff1a; 注入数据库名&#xff1a; 注入表名&#xff1a; 注入列明&#xff1a; 注入具体值&#xff1a;http://sqli-labs:8084/less-32/?id-1%df%27unio…

luceda ipkiss教程 66:金属线的钝角转弯

案例分享&#xff1a;金属线的135度转弯&#xff1a; 所有代码如下&#xff1a; from si_fab import all as pdk import ipkiss3.all as i3 from ipkiss.geometry.shape_modifier import __ShapeModifierAutoOpenClosed__ from numpy import sqrtclass ShapeManhattanStub(__…

《ESP8266通信指南》11-Lua开发环境配置

往期 《ESP8266通信指南》10-MQTT通信&#xff08;Arduino开发&#xff09;-CSDN博客 《ESP8266通信指南》9-TCP通信&#xff08;Arudino开发&#xff09;-CSDN博客 《ESP8266通信指南》8-连接WIFI&#xff08;Arduino开发&#xff09;&#xff08;非常简单&#xff09;-CSD…

短信公司_供应群发短信公司

短信公司——供应群发短信公司 短信公司作为一种为企业提供群发短信服务的服务商&#xff0c;正逐渐受到市场的青睐。供应群发短信公司作为其中的一种类型&#xff0c;为各行各业的企业提供高效、便捷的短信推广渠道。本文将介绍短信公司的作用以及供应群发短信公司的特点和优势…

Django之创建Model以及后台管理

一&#xff0c;创建项目App python manage.py startapp App 二&#xff0c;在App.models.py中创建类&#xff0c;以下是示例 class UserModel(models.Model):uid models.AutoField(primary_keyTrue, auto_createdTrue)name models.CharField(max_length10, uniqueTrue, db…

ICode国际青少年编程竞赛- Python-2级训练场-坐标与列表练习

ICode国际青少年编程竞赛- Python-2级训练场-坐标与列表练习 1、 for i in range(6):Spaceship.step(Item[i].x - Spaceship.x)Dev.step(Item[i].y - Dev.y)Dev.step(Spaceship.y - Dev.y)2、 for i in range(5):Spaceship.step(Item[i].x - Spaceship.x)Flyer[i].step(Item[…

车载测试___面试题和答案归纳

车载面试题 一、实车还在设计开发阶段&#xff0c;大部分测试通过什么测试&#xff1f; 答案&#xff1a;通过台架和仿真来完成的 二、测试部分划分&#xff1f; 测试部门是分为自研&#xff0c;系统&#xff0c;验收&#xff0c;自研部门是开发阶段测试&#xff0c;系统部门…

重发被恶意举报的主食冻干测评,速看可能再被删!PR、希喂和SC真实对比PK!

要给猫咪提供高品质主食&#xff0c;主食冻干是不二之选。主食冻干不仅含肉量高、吸收消化率高&#xff0c;还有着丰富的、普通猫粮无法提供的各类营养素&#xff0c;满足猫咪微量元素的需求。可以说是营养与生骨肉喂养媲美&#xff0c;又能完美避开生骨肉细菌超标带来的一系列…

如何优雅的实现接口限流?

首先限流&#xff0c;其实解决方案有很多&#xff0c;比如通过nginx配置&#xff0c;通过gateway网关进行限流&#xff0c;比如Spring Cloud GateWay整合熔断器实现限流 但是以上都是全局的&#xff0c;如何灵活的针对某些接口进行不同级别的限流呢&#xff1f; 方案一&#…

超标量处理器设计:重排序缓存(ROB)

★超标量处理器的很多地方用到了重排序缓存&#xff0c;但是我对它不是很了解&#xff0c;所以我整理一下重排序缓存的知识点。 重排序缓存(ROB)在确保乱序执行的指令能够正确地完成和提交(Commit)&#xff0c;也可以用来寄存器重命名。 ROB是一个先进先出的表&#xff0c;每个…

Re_Lasso

from sklearn.linear_model import LassoCV, Lasso import pandas as pd from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score from sklearn.model_selection import GridSearchCV# 读取数据…