【数据结构】线性表之单链表

news2024/11/25 8:18:54

目录

一、链表的概念

1、概念

2、分类

3、重点

二、单链表模拟实现

1、准备

2、头插法

3、尾插法

 4、指定下标插入

5、遍历

6、删除第一次出现的元素key

7、删除所有的key

8、双指针删除所有的key


一、链表的概念

1、概念

是一种物理存储结构上非连续的存储结构,但逻辑上是连续的一种数据结构

2、分类

单向或双向

带头或者不带头

循环或者非循环

3、重点

后续重点学习无头单向非循环链表也就是单链表,他结构简单,一般不单独使用存储数据而是常作为其他数据结构的子结构比如哈希桶、图的临近表。还有一个是双向非循环链表,Java集合类里的LinkedList底层就是这一数据结构

二、单链表模拟实现

1、准备

一个单链表有多个节点,而每个节点都有一个存放数据的地方,我们叫他数据域,还有一个存下一个节点地址的地方,我们叫他指针域

public class SingleLinkedList {
    static class Node{
        public int val;   //数据域
        public Node next; //指针域

        /**
         * 构造节点
         * @param val  节点的数据
         */
        public Node(int val) {
            this.val = val;
        }
    }
}

2、头插法

先创建节点,创建节点后,将节点的指针域指向原来链表的头节点

此时需要更新头节点的位置,让头节点指向新增的节点 

 

 

/**
     * 头插法
     * @param data  数据
     */
    public void addFirst(int data){
        Node node = new Node(data);  //创建节点

        node.next = head;            //将该节点的指针域指向头节点
        head = node;                 //再将头指针指向这个新的节点完成头插
    }

3、尾插法

尾插法分两种情况:1.链表有节点,此时只需找到最后一个节点,然后将最后一个节点的指针指向插入的节点即可

再找最后一个节点时要注意,再循环里让cur遍历时,循环结束的条件不能是cur != null此时当循环结束,cur不是指向最后一个节点而是null。要找到最后一个节点 循环结束的条件是cur.next != null

2.第二个情况则是,如果此时链表没有任何元素,插入的为第一个元素,则直接让头指针指向他即可

/**
     * 尾插法
     * @param data  尾插的数据
     */
    public void addLast(int data){
        Node node = new Node(data);   //创建节点
        
        if(head == null){             //如果此时没有任何节点,则直接将头节点指向他
            head = node;
            return;
        }
        
        Node cur = head;              //代替头节点遍历链表直到最后一个节点时停止

        while(cur.next != null){
            cur = cur.next;
        }
        
        cur.next = node;              //此时cur指向最后一个节点,此时只需将他的指针域指向新的节点即可
    }

 4、指定下标插入

指定的位置插入数据,比如在2下标插入数据,此时我们先得找到前一个。让前一个节点的next指向新增的节点,然后将新节点的next指向第二个节点,不过此处要注意如果我们先让2的前一个节点指向新的节点,在第二部将新节点的next指向2的时候会发现丢失了,所以此处可以先让新节点的next指向2位置的节点,在让前面的节点next指向新的节点,综上所述,分为以下主要步骤:找到前驱节点,让新增节点的next指向前驱节点的下一个节点,然后再将前驱节点的next 指向新增节点

 

 

 

 /**
     * 中间指定位置插入
     * @param index  指定插入下标
     * @param data   插入的数据
     */
    public void addIndex(int index,int data){
        //1.判断下标是否合法
        if(index < 0 || index > size()){
            throw new ArrayIndexOutOfBoundsException("非法下标");
        }

        //2.处理特殊情况
        if(index == 0){
            addFirst(data);                  //如果插头则调用头插
            return;
        }

        if(index == size()){
            addLast(data);                   //如果插尾则调用尾插
            return;
        }

        //3.找到前驱节点
        Node pre = searchPrevNodeToAdd(index);  //找到前一个节点

        //4.开始插入
        Node node = new Node(data);       //创建节点
        node.next = pre.next;             //让新增节点的next指向前驱节点的下一个
        pre.next = node;                  //再将前驱节点的next指向新增节点
    }

    /**
     * 新增时找前驱节点
     * @param index   待插入的下标
     * @return        前驱节点
     */
    private Node searchPrevNodeToAdd(int index) {
        Node cur = head;

        while(index-- != 1){
            cur = cur.next;
        }

        return cur;
    }

5、遍历

链表的遍历,定义一个指针代替头节点进行循环遍历 

6、删除第一次出现的元素key

要删除node这个节点时,我们需要先找到他的前一个节点,然后让他前一个节点指向他的后一个节点,所以现在我们先需要找到前一个节点,然后再删除。


    /**
     * 删除节点时找前驱节点
     * @param key  关键字
     * @return     前驱节点
     */
    private Node searchPrevNodeToRemove(int key) {
        Node cur = head;
        while(cur.next != null){         //要找的是前驱节点
            if(cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }

        return null;             //没找到返回空
    }

此处找前驱节点的代码里遍历链表退出循环的条件使用的是cur.next != null此处要注意,如果使用cur!=null,链表遍历到最后一个节点时cur!=null满足条件进入循环里的if cur.next.val由于是最后一个节点所以他的next是空的,此时空的再去访问val则会空指针异常 

 找到前驱节点后进行删除,要注意处理看头节点是不是符合删除的条件

/**
     * 删除第一次出现的指定节点
     * @param key
     */
    public void remove(int key){
        //1.是否为空
        if(head == null){
            return;
        }

        //2.判断头节点是不是
        if(head.val == key){
            head = head.next;
            return;
        }

        //3.找到前一个节点
        Node pre = searchPrevNodeToRemove(key);
        if(pre == null) return;

        //4.正常删除
        pre.next = pre.next.next;
    }

    /**
     * 删除节点时找前驱节点
     * @param key  关键字
     * @return     前驱节点
     */
    private Node searchPrevNodeToRemove(int key) {
        Node cur = head;
        while(cur.next != null){         //要找的是前驱节点
            if(cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }

        return null;             //没找到返回空
    }

7、删除所有的key

删除所有的key,我们可以定义一个指针指向头节点,让他去遍历链表,遍历的过程如果有满足条件的就删除

/**
     * 删除所有的key
     * @param key
     */
    public void removeAll(int key){
        //1.判断是否空
        if(head == null){
            return;
        }

        //2.遍历删除
        Node cur = head;
        while(cur.next != null){
            if(cur.next.val == key){
                cur.next = cur.next.next;
                continue;
            }
            cur = cur.next;
        }

        //3.处理头节点
        if(head.val == key){
            head = head.next;
        }

    }

上述代码要注意的是,处理头节点要放在遍历删除之后,如果头节点满足条件第二个节点也满足条件,此时删除了头进入循环时新的头节点也是key,但是循环里的逻辑是不能删除满足条件的头节点的,所以放在后面进行处理。而在上面只删除第一个key,把处理头节点放在循环前的原因是他只是删除第一个分先后顺序删除

8、双指针删除所有的key

定义两个指针,一个作为遍历指,一个作为遍历指针的前驱指针,让遍历指针遍历链表的同时进行判断他当前的节点是否满足删除条件,如果满足则前驱节点的next指向当前节点的next直接跳过当前节点删除,然后遍历指针指向下一个节点,如果不满足则让前驱节点指向当前节点,当前节点指向下一个节点

 /**
     * 双指针删除所有的key
     * @param key
     */
    public void removeAllByDoublePointer(int key){
        //1.判空
        if(head == null){
            return;
        }

        //2.双指针删除
        Node pre = head;
        Node cur = head.next;
        while(cur != null){
            if(cur.val == key){
                pre.next = cur.next;
            }else{
                pre = cur;
            }
            cur = cur.next;
        }

        //3.处理头节点
        if(head.val == key){
            head = head.next;
        }

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

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

相关文章

PS-历史记录

目录 哪里能找到【历史记录】面板 1、窗口→历史记录 2、编辑→清理→历史记录 还原 1、点击【历史记录】面板 快捷键 【ctrlz】 【shiftctrlz】 从当前状态创建新文档 创建新快照 给快照起名 1、右击你要创建快照的步骤 2、点击面板菜单 3、先按住alt不动&#…

Java 对象和类

Java作为一种面向对象语言。支持以下基本概念&#xff1a; 多态继承封装抽象类对象实例方法重载 本节我们重点研究对象和类的概念。 对象&#xff1a;对象是类的一个实例&#xff08;对象不是找个女朋友&#xff09;&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对…

猿如意|chat GPT测评

文章目录猿如意猿如意传送门猿如意个人使用感受好的一面&#xff1a;可以改进的一面:什么是猿如意chat GPT测评chat GPT使用过程使用场景描述&#xff1a;问题1问题2问题3问题4&#xff1a;问题5&#xff1a;主观感受&#xff1a;认为此功能不足的地方&#xff1a;对此功能的期…

学习编程的过程中可能会走哪些弯路,有哪些经验可以参考?

很多人学习编程, 走的弯路可以总结为以下几点: 一言不合找视频&#xff0c;几十集视频刷半年。 很多人学习编程的时候&#xff0c;喜欢看视频学&#xff0c;我这里总结一下看视频学习编程的弊端。 1. 完善的视频资源往往稍稍过时&#xff0c;比如你会发现很多java的教学视频…

产品设计市场调研有哪些特点?

产品市场种类繁多&#xff0c;变化无常&#xff0c;消费者需求各异。在工业设计之初&#xff0c;需要对行业和区域环境进行调查分析&#xff0c;深入了解市场情况、市场供求关系、客户引导、趋势等&#xff0c;客观合理地对新产品进行适当定位。只有有了正确的新产品概念规划方…

三方接口签名验签简易设计与实现

本人水平有限&#xff0c;对密码学的理解相当浅显。错误与疏漏&#xff0c;欢迎各位指正。 〇、写在前面 接口安全防护是个永恒的话题&#xff0c;提供给前端的接口需要登录&#xff0c;提供给服务的接口(下文简称"三方接口")也需要鉴权&#xff1b;当前大环境下,ht…

chatgpt教我内存对齐,对齐了但没完全对齐?

文章目录内存对齐关于chatgpt的回答总结内存对齐 关于chatgpt的回答 我与chatgpt的对话如下&#xff1a; 我现在来描述与总结上述对话都干了啥以及我为什么要问这个。 我本来是在学习rapidjson源码里面的内存池实现&#xff0c;然后 RAPIDJSON_ALIGN 没有看懂&#xff0c;所…

JSP课设:家庭相册管理系统(附源码+调试)

JSP家庭相册管理系统 &#xff08;1&#xff09;登录模块&#xff1a;分为普通用户和管理员两种角色&#xff1b; &#xff08;2&#xff09;普通用户模块&#xff1a;相册管理&#xff1a;用户可以对自己相册进行编辑&#xff0c;可以进行批量删除相册、新增相册、编辑相册以…

【Golang】案例为基浅谈Go的变量与常量

&#x1f4d3;推荐网站(不断完善中)&#xff1a;个人博客 &#x1f4cc;个人主页&#xff1a;个人主页 &#x1f449;相关专栏&#xff1a;CSDN专栏、个人专栏 &#x1f3dd;立志赚钱&#xff0c;干活想躺&#xff0c;瞎分享的摸鱼工程师一枚 &#x1f352;前言 在上一篇文章中…

Python图像识别实战(三):基于OpenCV实现批量单图像超分辨重建(附源码和实现效果)

前面我介绍了可视化的一些方法以及机器学习在预测方面的应用&#xff0c;分为分类问题&#xff08;预测值是离散型&#xff09;和回归问题&#xff08;预测值是连续型&#xff09;&#xff08;具体见之前的文章&#xff09;。 从本期开始&#xff0c;我将做一个关于图像识别的…

“命悬一线”复试上岸浙大MBA的经验分享~

作为去年踩线上岸浙大MBA的幸运者&#xff0c;希望自己的一些经历和经验能够帮助到今年的考生。因为去年在联考初试准备的时间不是很充分&#xff0c;加上在职工作相对比较忙&#xff0c;真正能用到学习上时间真的不多。笔试成绩只拿到191分&#xff0c;在去年的复试考生里算是…

Qt+C++基本绘图(画线,画圆,矩形, 撤销,重做)

程序示例精选 QtC基本绘图(画线&#xff0c;圆&#xff0c;矩形画线&#xff09; 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《QtC基本绘图(画线&#xff0c;画圆&#xff0c;矩形, 撤销&am…

Redis常见面试题(四)

1、Redis有哪些淘汰策略? Redis目前有8种淘汰策略: 1&#xff09;Volatile-lru: 设置了过期时间的Key使用了LRU算法淘汰; 2&#xff09;Allkeys-lru: 所有key使用LRU算法; 3&#xff09;Volatile-lfu: 设置了过期时间的key使用了LFU算法淘汰; 4&#xff09;Allkeys-lfu: …

一文读懂页面布局

一. 前端布局 谈到浏览器页面&#xff0c;我们肯定是希望页面越美观越好&#xff0c;这样才能吸引用户点击。页面美观就自然需要用到各种布局&#xff0c;好的布局不仅能提高用户体验感留住用户&#xff0c;还能提高维护的效率。本文就列举一下常用的前端布局&#xff0c;并配上…

【MATLAB教程案例65】深度学习网络建模2,通过deepNetworkDesigner工具箱实现

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 1.软件版本 2.分类识别数据库的应用

第八章练习题-2

第七题 答案 package com.hspedu.homework.homework07;public class HomeWork07 {public static void main(String[] args) {} } class Test{ //父类String name "Rose";Test(){System.out.println("Test");}Test(String name){this.name name;} } clas…

国考省考行测:标题选择题,仍然是考主旨,借助关联词、主体、结构、或对策分析法找正确的标题,通过偏颇,片面,无关,过重,空泛的方法排除错误选项

国考省考行测&#xff1a;标题选择题&#xff0c;仍然是考主旨&#xff0c;借助关联词、主体、结构、或对策分析法找正确的标题&#xff0c;通过偏颇&#xff0c;片面&#xff0c;无关&#xff0c;过重&#xff0c;空泛的方法排除错误选项 2022找工作是学历、能力和运气的超强…

[附源码]Python计算机毕业设计Django校园商铺

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

44_外部SRAM实验

目录 IS62WV51216简介 IS62WV51216框图 IS62WV51216读时序 IS62WV51216写时序 FSMC简介 FSMC寄存器介绍 硬件连接图 实验源码 IS62WV51216简介 IS62WV51216ISSi (Integrated Silicon Solution,Inc)公司生产的一颗16位宽512K (512*16,即1M字节)容量的CMOS静态内存(SRAM…

现在市场上IT人才大量涌现,现在开始学习编程,以后会有前途吗?

现在市场上IT人才大量涌现&#xff0c;如果我现在开始学习编程&#xff0c;以后会有前途吗&#xff1f;答案是肯定的&#xff0c;一定有前途而且前途远大&#xff01;下面分析一下具体原因&#xff1a; 未来一定是信息化社会 当今我们正处在第三次信息化浪潮中&#xff0c;这次…