手敲单链表,简单了解其运行逻辑

news2024/11/18 17:45:51

1. 链表

        1.1 结构组成

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

        链表的结构如下图所示,是由很多个节点相互通过引用来连接而成的;每一个节点由两部分组成,分别数据域(存放当前节点的数据)和next域(用来存放连接下一个节点的引用);

                    

        下图是链表的结构,每一个节点都有一个地址,方便前一个节点的next域来存放。多个节点通过引用连接成整个链表。

         实际在内存中每个节点的地址是随机的,只不过用这个节点的next,找到了下一个节点的地址,由此实现链接。

1.2 链表分类

        主要通过链表方向,是否循环,是否带箭头主要分为以下六个特色;

         

        下面是一些不同种类的链表图解:

        1. 单向或者双向

                      

         2. 带头或者不带头

                                 

        3. 循环或者非循环 

                             

        将以上六种单一种类进行组合可以构成一下8种链表

        虽然有这么多的链表的结构,但是我们重点掌握两种:

        无头单向非循环链表:结构简单,一般不会单独用来存数据

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

2.无头单向非循环链表的实现

2.1 自定义MyArrayList类

       建立一个Ilist接口,在里面构造mysinglelist链表要实现的抽象方法;

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

        无头单向非循环链表的节点是由两个属性(value域和next域构成的),同时也要在自定义MyArrayList类里面使用内部类创建链表节点类,之后再链表类里面创建一个头结点来代表当前链表的引用;同时实现我们之前创建的接口,接下来重写接口里面的方法,让其能够具体化;

public class MySingleList implements IList {
    //创建链表节点
    //节点的内部类
    static class ListNode{
        public int value;
        public ListNode next;//表示下一个节点的引用

        public ListNode(int value){
            this.value = value;
        }
    }
    public ListNode head;
    @Override
    public void addFirst(int data) {

    }

    @Override
    public void addLast(int data) {

    }

    @Override
    public void addIndex(int index, int data) {

    }

    @Override
    public boolean contains(int key) {
        return false;
    }

    @Override
    public void remove(int key) {

    }

    @Override
    public void removeAllKey(int key) {

    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public void clear() {

    }

    @Override
    public void display() {

    }
}
}

        下面代码主要是创建多个节点 

public void createList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        this.head = node1;
    }

2.2 遍历链表

        思路:

        1、当前节点是怎么走到下一个节点的

        2、当遍历链表时,如何判断当前链表的所有节点都遍历完

         首先建立一个当前节点cur,通过cur来指向next域里面的节点地址并访问和输出操作来完成整个链表的遍历;让cur的next域指向(存放)下一个节点的地址并访问,以此类推逐步完成整个链表的遍历(问题一);如果cur指向的下一个节点的next域里存放的不是地址,而是空指针,则当前的链表被遍历即将结束(问题二);

        下面是重写的遍历链表具体的方法:

 @Override
    public void display() {
        ListNode cur = head;
        while (cur != null){
            System.out.print(cur.value+"->");
            cur = cur.next;
        }
        System.out.println(" ");
    }
 public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
    }

        下面代码的执行结果:

2.3 得到单链表的长度

        对整个链表进行遍历,使用计数器进行记录遍历的次数,最后将计数器的值返回即可,下图代码是该方法的具体实现;

 @Override
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }
public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
        System.out.println(list.size());
    }

        下面代码的执行结果:

 2.4 查找是否包含关键字

        对链表进行遍历,然后将关键字key和链表数值进行比较,如果存在key关键字则返回true;反之则返回false;

        方法具体实现的代码如下:

 @Override
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null){
            if ( cur.value==key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

        测试代码和执行结果如下:

public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
        System.out.println(list.contains(45));
    }

           

2.5头插法

        思路:

        1、将之前第一个节点的地址存储到我们新添加的节点的next域里面;

        2、将新添加的节点赋给head,作为新链表的头节点,链表图解如下图所示:

        具体实现头插法的方法如下:

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

        测试代码及结果:

public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
        list.addFirst(1);
        list.addFirst(0);
        list.display();
    }

          

 2.6尾插法

        思路:

        1、首先对该链表进行遍历,当遍历到最后一个节点时,将新添加的节点的地址给最后一个节点的next域。

        2、如果该链表为空,直接将该新增节点设为头节点

        链表图解:

        具体实现方法带吗如下:

    @Override
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        ListNode cur = this.head;
        if (this.head == null){
            this.head = node;
        }else {
            //一直找最后一个节点
            while (cur.next != null){
                cur = cur.next;
            }
            cur.next = node;
        }
    }

          测试代码及结果:

public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
        list.addLast(9);
        list.addLast(10);
        list.display();
    }

 

        分析总结:头插法的时间复杂度为o(1);尾插法的时间复杂度为o(N);

2.7任意位置插入

        思路:

        1、需要插入的位置必须为合法,如果不合法,我们会抛出一个异常进行提醒,所以首先自定义一个异常;

public class ListIndexOutOfException extends RuntimeException{
    public ListIndexOutOfException(String msg){
        super(msg);
    }
}

        2、任意位置插入,首先分几种情况,插在开头,插在结尾,插在中间

        2.1 当插在链表开头和结尾时,可以使用头插法和尾差法;

        2.2 当插在其他的位置时,首先让cur走到index前面一个节点的位置(此处创建一个方法)(这时候就需要考虑将下一个节点加在index的位置时如何处理建立连接的顺序);其次注意建立连接的时候,一定要先建立添加节点和后节点的连接,其次在确立添加节点和前一个节点的位置,链表图解如下:

        具体实现方法代码如下:

 @Override
    public void addIndex(int index, int data) {
        if(index < 0 || index > size()) {
            //抛自定义的异常
            throw new ListIndexOutOfException("你当前输入的索引有问题");
        }
        if(index == 0) {
            addFirst(data);
            return;
        }
        if(index == size()) {
            addLast(data);
            return;
        }
            ListNode cur = searchPrev(index);
            //node之前的一个节点
            ListNode node = new ListNode(data);
            node.next = cur.next;
            cur.next = node;
        }


        private ListNode searchPrev(int index) {
            //该方法是找到添加节点node在index时
            //index之前的节点的索引
            ListNode cur = this.head;
            int count = 0;
            while (count != index-1 ) {
                cur = cur.next;
                count++;
            }
            return cur;
        }

        测试代码及结果如下:

 public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
        list.addIndex(2,2);
        list.addIndex(3,3);
        list.display();
    }

 

2.8删除第一次出现关键字为key的节点

        思路:大体分为以下四种情况

        1.链表为空链表,一个节点也没有
        2.我们所需要删除数据所在的节点在第一个
        3.遍历完所有的链表节点,发现没有要删除的数据
        4.有要删除的数据且不是第一个节点

        具体实现方法代码如下:

public void remove(int key) {
        if(this.head == null) {
            //一个节点都没有 无法删除!
            return;
        }
        if(this.head.value == key) {
            this.head = this.head.next;
            return;
        }
        //1. 找到前驱
        ListNode cur = findPrev(key);
        //2、判断返回值是否为空?
        if(cur == null) {
            System.out.println("没有你要删除的数字");
            return;
        }
        //3、删除
        ListNode del = cur.next;
        cur.next = del.next;
    }

    private ListNode findPrev(int key) {
        //找到要删除节点的前一个节点
        ListNode cur = this.head;
        while (cur.next != null) {
            if(cur.next.value == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

        测试代码及结果如下:

public static void main(String[] args) {
        MySingleList list = new MySingleList();
            list.createList();
            list.display();
        list.addIndex(2,2);
        list.addIndex(3,3);
        list.remove(100);
        list.display();
    }

2.9回收链表

        将头节点置为空即可,代码和结果如下所示;

 @Override
    public void clear() {
        this.head = null;
    }

 

ps:本次的内容就到这里了,如果你喜欢的话,就请一键三连哦!!!

 

 

        

 

 

 

 

      

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

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

相关文章

【LeetCode每日一题合集】2023.11.20-2023.11.26 (二叉树中的伪回文路径)

文章目录 53. 最大子数组和解法1——DP解法2——分治&#xff08;维护区间、类似线段树的思想&#xff09; 2216. 美化数组的最少删除数&#xff08;贪心&#xff09;2304. 网格中的最小路径代价1410. HTML 实体解析器&#xff08;模拟&#xff09;2824. 统计和小于目标的下标对…

iOS Class Guard 成功了,但无法区分差异

​ 我正在开发一个静态库&#xff0c;并使用 Polidea 的 iOS Class Guard 来混淆我的静态库。我按照步骤在项目的根路径中下载 obfuscate_project&#xff0c;更改其中所需的名称&#xff0c;最后在终端中运行 bash obfuscate_project。我收到一条消息&#xff0c;说我的构建成…

【漏洞复现】大华智慧园区综合管理平台deleteFtp接口远程命令执行

漏洞描述 大华智慧园区综合管理平台deleteFtp接口存在远程命令执行,攻击者可利用该漏洞执行任意命令,获取服务器控制权限。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益…

CMMI认证含金量高吗

一、CMMI认证含金量解答 CMMI&#xff0c;即能力成熟度模型集成&#xff0c;是由美国卡内基梅隆大学软件工程研究所开发的一种评估企业软件开发过程成熟度的模型。CMMI认证的含金量究竟高不高呢&#xff1f;答案是肯定的。CMMI认证被誉为软件开发行业的“金牌标准”&#xff0…

spring cloud gateway源码分析,一个请求进来的默认处理流程

1.前言 spring cloud gateway的基本组成和作用就不细赘述&#xff0c;此篇适合对此有一定了解的人阅读。 spring cloud gateway版本: Hoxton.SR1 spring cloud gateway的配置使用yml配置&#xff1a; server:port: 9527y#根据微服务名称进行动态路由的配置 spring:applicati…

zookeeper心跳检测 (实操课程)

本系列是zookeeper相关的实操课程&#xff0c;课程测试环环相扣&#xff0c;请按照顺序阅读来学习和测试zookeeper。 阅读本文之前&#xff0c;请先阅读----​​​​​​zookeeper 单机伪集群搭建简单记录&#xff08;实操课程系列&#xff09;zookeeper 客户端常用命令简单记录…

人工智能-优化算法之学习率调度器

学习率调度器 到目前为止&#xff0c;我们主要关注如何更新权重向量的优化算法&#xff0c;而不是它们的更新速率。 然而&#xff0c;调整学习率通常与实际算法同样重要&#xff0c;有如下几方面需要考虑&#xff1a; 首先&#xff0c;学习率的大小很重要。如果它太大&#xf…

知识管理平台Confluence:win10安装confluence

文章目录 介绍主要功能 安装教程安装java运行平台JRE安装数据库Postgresql在Postgresql创建confluence使用的数据库创建数据库用户创建数据库 安装confluence注册confluence启动confluence 参考链接 介绍 Confluence 是由澳大利亚软件公司 Atlassian 开发的企业协作平台。它提…

flutter开发实战-ValueListenableBuilder实现局部刷新功能

flutter开发实战-ValueListenableBuilder实现局部刷新功能 在创建的新工程中&#xff0c;点击按钮更新counter后&#xff0c;通过setState可以出发本类的build方法进行更新。当我们只需要更新一小部分控件的时候&#xff0c;通过setState就不太合适了&#xff0c;这就需要进行…

canvas基础:渲染文本

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

java设计模式学习之【桥接模式】

文章目录 引言桥接模式简介定义与用途&#xff1a;实现方式 使用场景优势与劣势桥接模式在Spring中的应用绘图示例代码地址 引言 想象你正在开发一个图形界面应用程序&#xff0c;需要支持多种不同的窗口操作系统。如果每个系统都需要写一套代码&#xff0c;那将是多么繁琐&am…

scrapy爬虫中间件和下载中间件的使用

一、关于中间件 之前文章说过&#xff0c;scrapy有两种中间件&#xff1a;爬虫中间件和下载中间件&#xff0c;他们的作用时间和位置都不一样&#xff0c;具体区别如下&#xff1a; 爬虫中间件&#xff08;Spider Middleware&#xff09; 作用&#xff1a; 爬虫中间件主要负…

SQL Server 2016(基本概念和命令)

1、文件类型。 【1】主数据文件&#xff1a;数据库的启动信息。扩展名为".mdf"。 【2】次要&#xff08;辅助&#xff09;数据文件&#xff1a;主数据之外的数据都是次要数据文件。扩展名为".ndf"。 【3】事务日志文件&#xff1a;包含恢复数据库的所有事务…

深入理解前端路由:构建现代 Web 应用的基石(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

2024年天津天狮学院专升本专业课报名缴费流程

天津天狮学院高职升本缴费流程 一、登录缴费系统 二、填写个人信息&#xff0c;进行缴费 1.在姓名处填写“姓名”&#xff0c;学号处填写“身份证号”&#xff0c;如下图所示&#xff1a; 此处填写身份证号 2.单击查询按钮&#xff0c;显示报考专业及缴费列表&#xff0c;…

JPA数据源Oracle异常记录

代码执行异常 ObjectOptimisticLockingFailureException org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleSta…

从0开始学习JavaScript--JavaScript ES6 模块系统

JavaScript ES6&#xff08;ECMAScript 2015&#xff09;引入了官方支持的模块系统&#xff0c;使得前端开发更加现代化和模块化。本文将深入探讨 ES6 模块系统的各个方面&#xff0c;通过丰富的示例代码详细展示其核心概念和实际应用。 ES6 模块的基本概念 1 模块的导出 ES…

java原子类型

AtomicBoolean AtomicInteger AtomicLong AtomicReference<V> StringBuilder - 不是原子类型。StringBuilder 是 java.lang 包下的类 用法&#xff1a;无需回调改变数值

基于springboot + vue框架的网上商城系统

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

Linux:vim的简单使用

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、vim的基本概念二、vim的基本操作三、vim正常模式命令集四、vim底行模式命令集五、.xxx.swp的解决总结 前言 本文是对Linux中vim使用的总结 一、vim的基本概念 …