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

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:本次的内容就到这里了,如果你喜欢的话,就请一键三连哦!!!

 

 

        

 

 

 

 

      

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 1205 每月交易2(PostgreSQL)

数据准备 create type state as enum(approved, declined); create table Transactions( id int, country varchar(4), state_enum state, amount int, trans_date date ); Create table If Not Exists Chargebacks ( trans_id int, trans_date date ); insert into Transac…

对小程序的初了解

WXML和HTML的区别 标签名称不同 HTML&#xff1a;div、a、span、img WXML&#xff1a;view、text、image、navigator 属性节点不同 <a href"#">超链接</a> <navigator url"/pages/home/home"></navigator> 提供了类似vue的…

HTTP协议、Java前后端交互、Servlet

文章目录 抓包工具 FiddlerHTTP 请求和响应结构URL 唯一资源定位符HTTP 协议中的方法请求报头&#xff08;header&#xff09;HTTP响应构造 HTTP 请求基于 form 标签基于 ajax使用 Postman HTTPS和 HTTP 的区别对称密钥和非对称密钥数字证书 TomcatServlet创建 Maven 项目引入依…

Linux基础项目开发1:量产工具——文字系统(四)

前言&#xff1a; 前面我们已经把显示系统&#xff0c;输入系统的框架搭建好了&#xff0c;那么有了输入和显示&#xff0c;显示的内容应该是什么呢&#xff1f;这节就要让我们一起对显示的内容&#xff0c;文字系统进行搭建。 目录 一、数据结构抽象 1.描述一个文字的位图&a…

西南科技大学(数据结构A)期末自测练习三

一、填空题&#xff08;每空1分&#xff0c;共10分&#xff09; 1、为解决计算机主机与打印机之间速度不匹配的问题&#xff0c;通常设置一个打印数据缓冲区。主机将要输出的数据依次写入缓冲区&#xff0c;打印机则依次从缓冲区中取出数据&#xff0c;则该换缓冲区的逻辑结构…

JIRA 基本使用

该页面可以&#xff1a; 查看个人基本信息以及归属的邮件组修改常用参数配置查看指给自己的 Open 问题查看自己最近的活动记录等 权限管理 Project 权限管理 JIRA 项目有三种通用权限方案&#xff1a; 公开权限方案&#xff08;默认禁止使用此方案&#xff09;&#xff1a…

FlowJo软件的简单介绍 掌控流式细胞分析的科技巨匠 FlowJo10

FlowJo 10 for Mac是一款强大的流式细胞数据分析软件&#xff0c;具有以下功能&#xff1a; 数据导入与预处理&#xff1a;FlowJo 10可以轻松导入各种类型的流式细胞数据&#xff0c;并对数据进行预处理&#xff0c;包括去噪、背景校正等&#xff0c;以确保数据的准确性和可靠…

视频怎么去水印?如何下载保存无水印视频?

你是否曾经在观看鬼畜素材视频时&#xff0c;被烦人的水印挡住了视线&#xff0c;让你感到十分郁闷&#xff1f;不要担心&#xff0c;今天我将为你介绍几种经典的方法&#xff0c;让你轻松下载无水印视频&#xff0c;让观看体验更加清爽不留痕迹。让我们一起来试试吧&#xff0…

vue实现数字千分位格式化 如6,383,993,037,937.463

1.封装文件&#xff1a;numberToCurrency.js /**实现数字千分位格式化 如6,383,993,037,937.463 */ export function numberToCurrencyNo(value) {if (!value) return 0// 获取整数部分const intPart Math.trunc(value)// 整数部分处理&#xff0c;增加,const intPartFormat …

牛客算法题 【HJ97 记负均正】 golang实现

题目 HJ97 记负均正 描述 首先输入要输入的整数个数n&#xff0c;然后输入n个整数。输出为n个整数中负数的个数&#xff0c;和所有正整数的平均值&#xff0c;结果保留一位小数。 0即不是正整数&#xff0c;也不是负数&#xff0c;不计入计算。如果没有正数&#xff0c;则平均…

如何访问电脑的组策略编辑器?

如何打开组策略 如果我们使用的是 Win 10 系统&#xff0c;如何打开组策略&#xff1f;下面为大家总结了四种打开组策略编辑器的方法。 从搜索框打开 Win 10 策略组怎么打开&#xff1f;一个简单快速的方法就是使用 Windows 自带的搜索栏。我们可以向搜索框中输入“编辑组策…

netty源码:(1)NioEventLoopGroup

EventLoopGroup bossGroup new NioEventLoopGroup(); 不加参数创建NioEventLoopGroup的话&#xff0c;会使用cpu核数*2作为bossGroup的线程数。

2023年阅读类APP如何发展?怎么做好商业化? | TopOn观察

前言 阅读类APP作为泛娱乐应用的重要板块&#xff0c;近年来在全球都发展火热。本文将主要从阅读类应用的市场规模、头部产品及地区特点、商业化模式及提升商业变现几个方面入手&#xff0c;解析2023年阅读类APP的发展趋势&#xff0c;希望为阅读类应用开发者带来参考价值。 一…

使用visual Studio MFC 平台实现对灰度图添加椒盐噪声,并进行均值滤波与中值滤波

平滑处理–滤波 本文使用visual Studio MFC 平台实现对灰度图添加椒盐噪声&#xff0c;并进行均值滤波与中值滤波 关于其他MFC单文档工程可参考 01-Visual Studio 使用MFC 单文档工程绘制单一颜色直线和绘制渐变颜色的直线 02-visual Studio MFC 绘制单一颜色三角形、渐变颜色边…

利用python连接MySQL数据库并执行相关sql操作

一、新建MySQL数据库 1.启动MySQL服务 打开phpstudy&#xff0c;开启MySQL服务。如果开启失败的话&#xff0c;可以打开任务管理器&#xff0c;把正在运行的mysqld服务的进程进行关闭&#xff0c;再次打开MySQL服务即可启动。 2.新建MySQL数据库 选择数据库&#xff0c;点击…

ORACLE同义词说明及使用

同义词概念 Oracle的同义词&#xff08;synonyms&#xff09;从字面上理解就是别名的意思&#xff0c;和视图的功能类似&#xff0c;就是一种映射关系。它可以节省大量的数据库空间&#xff0c;对不同用户的操作同一张表没有多少差别;它扩展了数据库的使用范围&#xff0c;能够…

基于SSM的经典电影推荐网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Python 流程控制

目录 程序流程 顺序结构 分支结构 单分支 双分支 多分支 if 嵌套 循环结构 while循环 for 循环 退出循环 循环与分支嵌套 附录 程序流程 程序是由语句构成&#xff0c;而流程控制语句 是用来控制程序中每条语句执行顺序的语句。可以通过控制语句实现更丰富的逻辑…

中国版的 GPTs:InsCode AI 生成应用

前言 在上一篇文章 《InsCode&#xff1a;这可能是下一代应用开发平台&#xff1f;》中&#xff0c;我们介绍了一个新的应用开发平台 InsCode&#xff0c;它是基于云原生开发环境 云 IDE AI 辅助编程的一站式在线开发平台。 最近&#xff0c;InsCode 又推出了另一种全新的开…

ASP.NET Core 使用IIS调试出现505.24错误

最近一直再学习asp.net 相关的东西&#xff0c;主要是为前端app提供一个webapi接口。在使用iis调试程序时出现HTTP Error 500.24 - Internal Server Error错误&#xff0c;搞了好久才最终解决。 1.在项目中增加web.config配置文件 2.将配置文件改为如下内容 <?xml version…