模拟实现链表的功能

news2024/12/26 23:08:00

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 和 cur
        ListNode 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.coloradmin.cn/o/1657340.html

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

相关文章

机器学习:基于线性回归、岭回归、xgboost回归、Lasso回归、随机森林回归预测卡路里消耗

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

小丑的身份证和复印件 (BFS + Floyd)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例&#xff1a; 输入 2 10 (JOKERjoke #####asdr) 输出 12 思路&#xff1a; 根据题意&#xff0c;要求最短时间&#xff0c;实际上也可以理解为最短距离。 所以应该联想到有关最短距离的算法&…

【图文教程】PyCharm安装配置PyQt5+QtDesigner+PyUic+PyRcc

这里写目录标题 PyQt5、Qt Designer、PyUic、PyRcc简介&#xff08;1&#xff09;下载安装PyQt5&#xff08;2&#xff09;打开designer.exe所在位置&#xff08;3&#xff09;在PyCharm中配置QtDesigner&#xff08;4&#xff09;验证QtDesigner是否配置成功&#xff08;5&…

重学java 34.API 5.工具类

有失才有悟&#xff0c;崩塌后的重建只会更牢固 —— 24.5.9 一、System类 1.概述: 系统相关类,是一个工具类 2.特点: a.构造私有,不能利用构造方法new对象 b.方法都是静态的 3.使用: 类名直接调用 4.方法 方法 …

Linux系统入侵排查(二)

前言 为什么要做系统入侵排查 入侵排查1 1.排查历史命令记录 2.可疑端口排查 3.可疑进程排查 4.开机启动项 4.1系统运行级别示意图&#xff1a; 4.2查看运行级别命令 4.3系统默认允许级别 4.4.开机启动配置文件 入侵排查2&#xff1a; 1.启动项文件排查&#xff1…

Python从0到POC编写--实用小脚本

UrlCheck&#xff1a; 假设我们要对一份 url 列表进行访问是不是 200 &#xff0c; 量多的话肯定不能一个一个去点开看&#xff0c; 这个时候我们可以借助脚本去判断&#xff0c; 假如有一份这样的列表&#xff0c; 这份列表呢&#xff0c;奇奇怪怪&#xff0c;有些写错了…

基于Spring Boot的公司OA系统设计与实现

基于Spring Boot的银行OA系统设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 用户登录界面&#xff0c;在银行OA系统运行后&#x…

刷题第3天(中等题):LeetCode24--两两交换链表中的节点--递归法

LeetCode24&#xff1a; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4…

FastDFS - 无法获取服务端连接资源:can‘t create connection to/xx.xx.xx.xx:0

问题描述 根据官方文档 安装完FastDFS服务器后&#xff0c; 服务正常启动&#xff0c;但是在 SpringBoot 项目使用 fastdfs-client 客户端报错无法获取服务端连接资源&#xff1a;cant create connection to/xx.xx.xx.xx:0, 一系列排查发现是获取到的 tracker 端口为 0 。 co…

Docx文件误删除如何恢复?别再花冤枉钱了,4个高效恢复软件!

不管是工作还是学习&#xff0c;总是会与各种各样的文件打交道。文件量越多就越容易出现文件丢失、文件误删的情况。遇到这些情况&#xff0c;失去的文件还能找回来吗&#xff1f;只要掌握了一些数据恢复方法&#xff0c;是很有机会恢复回来的&#xff0c;下面我会将这些方法分…

生信分析进阶2 - 利用GC含量的Loess回归矫正reads数量

在NGS数据比对后&#xff0c;需要矫正GC偏好引起的reads数量误差可用loess回归算法&#xff0c;使用R语言对封装的loess算法实现。 在NIPT中&#xff0c;GC矫正对检测结果准确性非常重要&#xff0c;具体研究参考以下文章。 Noninvasive Prenatal Diagnosis of Fetal Trisomy…

static静态成员变量和静态方法

当有new创建一个对象的,里面属性和方法,通过构造函数,能定义多个不同的对象,在我们做面向对象开发的时候,给一个场景,人在一个班级的时候,你的老师可能是固定的。 当我们用构造方法去构造的时候&#xff0c;每次都去传递一个固定的实参去定义个老师。 这样好会显得代码非常的…

DNS 解析在网络传输中有什么意义?

首先我们先说说什么是DNS解析&#xff1f; DNS解析是将域名解析为对应的IP地址的过程。DNS它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS解析的过程就是寻找哪个IP地址对应你所输入的网址&#xff0c;然后将网页内容返回给用户…

常用的文件摆渡系统有哪些 | 好用的文件摆渡系统推荐

一、什么是文件摆渡系统 简单来说&#xff0c;文件摆渡系统是一种高效的、以文件为中心的文件管理系统&#xff0c;它的出现旨在解决企业在文件传输、共享和管理过程中的种种痛点。 更为值得一提的是&#xff0c;文件摆渡系统还具备强大的安全合规性&#xff0c;能够有效防止…

MultiBooth:文本驱动的多概念图像生成技术

在人工智能的领域&#xff0c;将文本描述转换为图像的技术正变得越来越先进。最近&#xff0c;一个由清华大学和Meta Reality Labs的研究人员组成的团队&#xff0c;提出了一种名为MultiBooth的新方法&#xff0c;它能够根据用户的文本提示&#xff0c;生成包含多个定制概念的图…

pytorch加载模型出现错误

大概的错误长下面这样&#xff1a; 问题出现的原因&#xff1a; ​很明显&#xff0c;我就是犯了第一种错误。 网上的修改方法&#xff1a; 我觉得按道理哈&#xff0c;确实&#xff0c;蓝色部分应该是可以把问题解决了的​。​但是我没有解决&#xff0c;因为我犯了另外一个错…

Django关于ORM的增删改查

Django中使用orm进行数据库的管理&#xff0c;主要包括以下步骤 1、创建model&#xff0c; 2、进行迁移 3、在视图函数中使用 以下的内容可以先从查询开始看&#xff0c;这样更容易理解后面删除部分代码 主要包括几下几种&#xff1a; 1、增 1&#xff09;实例例化model,代…

struct和union大小计算规则

Union 一&#xff1a;联合类型的定义 联合也是一种特殊的自定义类型&#xff0c;这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09; 比如&#xff1a;共用了 i 这个较大的空间 二&#xff1a; 联合的…

每日Attention学习4——Spatial Attention Module

模块出处 [link] [code] [MM 21] Complementary Trilateral Decoder for Fast and Accurate Salient Object Detection 模块名称 Spatial Attention Module (SAM) 模块作用 空间注意力 模块结构 模块代码 import torch import torch.nn as nn import torch.nn.functional a…

CTFHUB-技能树-Web题-RCE(远程代码执行)-eval执行

CTFHUB-技能树-Web题-RCE&#xff08;远程代码执行&#xff09; 文章目录 CTFHUB-技能树-Web题-RCE&#xff08;远程代码执行&#xff09;eval执行解题方法&#xff1a;构造网址&#xff0c;查找当前目录文件并没有发现flag,接着查看上一级目录接着查看上一级接着查看上一级目录…