链表(LinkedList) 1

        上期内容我们讲述了顺序表,知道了顺序表的底层是一段连续的空间进行存储(数组),在插入元素或者删除元素需要将顺序表中的元素整体移动,时间复杂度是O(n),效率比较低。因此,在Java的集合结构中又引入了链表来解决这一问题。

1、链表 

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

        如上图所示是单向链表节点的两个元素:其中value存储着节点的值,next存储着下一个节点的地址。因此,一个单向链表可以表示为下图所示:

注意 :

1、从上图可以看出,链式结构在逻辑上是连续的,但在物理上(即在计算机的内存里面)不一定连续。

2、 现实中的节点一般是从堆上申请出来的。

3、从堆上申请的空间,是按照一定策略来分配的,两次申请的空间可能连续,也可能不连续。

2、链表结构

        链表组合起来的结构一共有8种,通过以下情况进行排列组合:

2.1 单向或者双向

        单向

        双向 

        双向链表在next域的基础上增加了prev域,使得通过链表的一个节点不仅能访问后继元素,也能访问前驱元素 。

2.2 带头或者不带头

        带头

        注意:这里的头并没有实际的值,主要用它链接后续的节点,因此,head指向第一个元素的地址

        不带头

2.3 循环或者非循环     

        循环

     

        非循环

        这里我们重点讲单向不带头非循环链表和双向不带头非循环链表。

3、无头单向非循环链表实现

3.1 节点的实现

        链表的节点主要通过静态内部类进行实现。代码如下:

  static class ListNode{
        public int val;//节点的值域
        public ListNode next;//下一个节点的地址

        public ListNode() {
        }

        public ListNode(int val) {
            this.val = val;
        }
    }
   public ListNode head;//表示当前链表的头结点

        这里可能有人会有疑问:为什么不把头结点的声明放入内部类中呢?

        其实,从逻辑上想,不难想明白:头结点是属于整个链表的头结点,而非结点的头结点。 

 3.2 creatNode方法

        本方法用于初始化一个链表

   public void creatNode(){
        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;
    }

3.3 头插法 

        故名思义:就是在链表的头部插入。这里有个点需要注意:先插入的数据会最后输出,后插入的最先输出。

        在头部插入一个元素,我们需要先绑定元素后面的信息,再让我们的头结点指向我们要插入的元素。代码如下:

  //头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);
        //一般建议在插入的时候先绑定后面的节点信息
        node.next = head;
        this.head = node;
    }

3.4 尾插法 

        在尾部插入一个元素,需要先绑定前面的元素的信息,即让最后一个元素的next指向要插入的元素。

        注意:如果链表中没有元素,则直接让头结点指向要插入的元素。

//尾插法
    public void addLast(int data){
        ListNode cur = head;
        ListNode node = new ListNode(data);
        if(cur == null){
            //如果链表中没有元素,那么让头结点指向node
            head = node;
            return;
        }
        //当cur.next == null的时候,那么该节点就是尾巴节点
        //当cur == null证明该链表已经被遍历完了
        while (cur.next != null){
            cur = cur.next;
        }
        cur.next = node;
    }

3.5  任意位置插入,第一个数据节点为0号下标

        在任意位置插入,首先,我们要判定下标是否合法,若不合法则抛出异常。第二步,如果插入的下标为0,则直接调用前面写的头插法函数,如果下标等于链表的长度,则调用尾插法函数。第三步,如果插入的地方是在中间,则需要先找到要插入结点的前一个结点,绑定后面的信息后再进行插入。

//任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        if(index < 0 || index > size()){
            throw new IndexErrorException("下标不合法");
        }
        if(index == 0){
            addFirst(data);
            return;
        }
        else if (index == size()){
            addLast(data);
            return;
        }
        //1、定义cur走index-1步
        //2、进行插入
        ListNode node = new ListNode(data);
        ListNode cur = head;
        int count = 0;
        while(count != index-1){
            cur = cur.next;
            count++;
        }
        node.next = cur.next;
        cur.next = node;
    }

3.6 查找关键字key是否在单链表当中

        查找关键字key只需要遍历链表,看结点的value是否等于key就ok了。

 //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        ListNode cur = head;
        while (cur != null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

3.7 删除第一次出现关键字key的元素 

        思路:删除一个结点(del),我们需要记录被删除结点的前一个结点(cur),再让前一个结点的next域(cur.next)指向被删除结点的下一个结点(del.next)。需要注意的是:在这个过程之前需要单独删除头结点。

//删除第一次出现关键字为key的节点
 public void remove(int key){
        if(head == null){
            return;
        }
        //单独删除头结点
        if(head.val == key){
            head = head.next;
            return;
        }
        //找到删除节点的前一个结点
        ListNode cur = head;
        while (cur.next != null)
        {
            ListNode del = cur.next;
            if(del.val == key){//此时cur指向的节点就是要删除节点的前一个节点
                    //cur.next = cur.next.next
                cur.next = del.next;
                return;//删除之后返回
            }
            cur = cur.next;
        }
        if(cur==null){
            System.out.println("没有你要删除的数字");
        }
    }

3.8  删除所有值为key的节点

        删除所有值为key的结点需要用到双指针的思想(ps:因为需要不断地记录被删除结点的前一个结点), prev指针用来找到被删除元素的前一个元素,cur指针则用来遍历链表和找到被删除元素。删除的过程就是直接让prev的next域直接指向cur指针的next域,再让cur指针向后走;若不是被删除的元素则直接让两个指针一起往后走。最后,我们需要单独删除头结点。(注意:这里我们在最后才删除头结点是因为前面的cur和prev指针需要使用到头结点)。

  //删除所有值为key的节点 
public void removeAllKey(int key){
        if(head == null){
            return;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        while (cur != null){
            if(cur.val == key){
                prev.next = cur.next;
                cur = cur.next;
            }else{
                prev = cur;
                cur = cur.next;
            }
        }
        //单独删除头结点
        if(head.val == key){
            head = head.next;
        }
    }

3.9 得到单链表长度

 得到单链表的长度,我们需要定义一个计数器,然后遍历链表让count++就行了。

 //得到单链表的长度
    public int size(){
        int count = 0;
        ListNode cur = head;
        while(cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }

3.10 清空链表

        遍历链表将链表的每个结点置空即可。

//清空链表  
public void clear() {
        ListNode cur = head;
        while (cur != null){
            cur = null;
            cur = cur.next;
        }
    }

3.11 打印链表

        遍历链表,打印每个结点的value域即可。

//打印链表
    public void display() {
        ListNode cur = head;
        while (cur!= null){//如果cur==null证明把链表遍历完成了
            System.out.print(cur.val + " ");
            cur = cur.next;//cur每次都在向后走一步
}
        System.out.println();
    }

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

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

相关文章

[手机Linux] onepluse6T 系统重新分区

一&#xff0c;刷入TWRP 1. 电脑下载 Fastboot 工具&#xff08;解压备用&#xff09;和对应机型 TWRP&#xff08;.img 后缀文件&#xff0c;将其放入前面解压的文件夹里&#xff09; 或者直接这里下载:TWRP 2. 将手机关机&#xff0c;长按音量上和下键 开机键 进入 fastbo…

活动预告 |【Part1】Microsoft 安全在线技术公开课:安全性、合规性和身份基础知识

课程介绍 通过参加“Microsoft 安全在线技术公开课&#xff1a;安全性、合规性和身份基础知识”活动提升你的技能。在本次免费的介绍性活动中&#xff0c;你将获得所需的安全技能和培训&#xff0c;以创造影响力并利用机会推动职业发展。你将了解安全性、合规性和身份的基础知识…

从零开始玩转Docker:轻松开启容器化之旅

一、什么是 Docker Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。简单来说&#xff0c;Docker 就像是一个超级 “快递箱”&#xff0c…

为何实现大语言模型的高效推理以及充分释放 AI 芯片的计算能力对于企业级落地应用来说,被认为具备显著的研究价值与重要意义?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ AI 芯片&#xff1a;为人工智能而生的 “大脑” AI 芯片&#xff0c;又称人工智能加速器或计算卡&#xff0c;是专为加速人工智能应用&#xff0c;特别是深度学习任务设计的专用集成电路&#xff08;A…

软件模拟I2C案例(寄存器实现)

引言 在经过前面对I2C基础知识的理解&#xff0c;对支持I2C通讯的EEPROM芯片M24C02的简单介绍以及涉及到的时序操作做了整理。接下来&#xff0c;我们就正式进入该案例的实现环节了。本次案例是基于寄存器开发方式通过软件模拟I2C通讯协议&#xff0c;然后去实现相关的需求。 阅…

【redis】数据类型之hash

Redis中的Hash数据类型是一种用于存储键值对集合的数据结构。与Redis的String类型不同&#xff0c;Hash类型允许你将多个字段&#xff08;field&#xff09;和值&#xff08;value&#xff09;存储在一个单独的key下&#xff0c;从而避免了将多个相关数据存储为多个独立的key。…

5.2Internet及其作用

5.2.1Internet概述 Internet称为互联网&#xff0c;又称英特网&#xff0c;始于1969年的美国ARPANET&#xff08;阿帕网&#xff09;&#xff0c;是全球性的网络。 互连网指的是两个或多个不同类型的网络通过路由器等网络设备连接起来&#xff0c;形成一个更大的网络结构。互连…

深度学习模型蒸馏技术的发展与应用

随着人工智能技术的快速发展&#xff0c;大型语言模型和深度学习模型在各个领域展现出惊人的能力。然而&#xff0c;这些模型的规模和复杂度也带来了显著的部署挑战。模型蒸馏技术作为一种优化解决方案&#xff0c;正在成为连接学术研究和产业应用的重要桥梁。本文将深入探讨模…

网络与数据安全

目录 数据加密对称加密&#xff08;Symmetric Encryption&#xff09;非对称加密&#xff08;Asymmetric Encryption&#xff09;哈希算法&#xff08;Hash Functions&#xff09;数字签名&#xff08;Digital Signature&#xff09;密钥管理&#xff08;Key Management&#x…

< OS 有关 > 利用 google-drive-ocamlfuse 工具,在 Ubuntu 24 系统上 加载 Google DRIVE 网盘

Created by Dave On 8Feb.2025 起因&#xff1a; 想下载 StableDiffusion&#xff0c;清理系统文件时把 i/o 搞到 100%&#xff0c;已经删除到 apt 缓存&#xff0c;还差 89MB&#xff0c;只能另想办法。 在网上找能不能挂在 Google 网盘&#xff0c;百度网盘&#xff0c;或 …

05vue3实战-----配置项目代码规范

05vue3实战-----配置项目代码规范 1.集成editorconfig配置2.使用prettier工具2.1安装prettier2.2配置.prettierrc文件&#xff1a;2.3创建.prettierignore忽略文件2.4VSCode需要安装prettier的插件2.5VSCod中的配置2.6测试prettier是否生效 3.使用ESLint检测3.1VSCode需要安装E…

【漫话机器学习系列】084.偏差和方差的权衡(Bias-Variance Tradeoff)

偏差和方差的权衡&#xff08;Bias-Variance Tradeoff&#xff09; 1. 引言 在机器学习模型的训练过程中&#xff0c;我们常常面临一个重要的挑战&#xff1a;如何平衡 偏差&#xff08;Bias&#xff09; 和 方差&#xff08;Variance&#xff09;&#xff0c;以提升模型的泛…

23.PPT:校摄影社团-摄影比赛作品【5】

目录 NO12345​ NO6 NO7/8/9/10​ 单元格背景填充表格背景填充文本框背景填充幻灯片背景格式设置添加考生文件夹下的版式 NO12345 插入幻灯片和放入图片☞快速&#xff1a;插入→相册→新建相册→文件→图片版式→相框形状→调整边框宽度左下角背景图片&#xff1a;视图→…

OpenCV:图像修复

目录 简述 1. 原理说明 1.1 Navier-Stokes方法&#xff08;INPAINT_NS&#xff09; 1.2 快速行进方法&#xff08;INPAINT_TELEA&#xff09; 2. 实现步骤 2.1 输入图像和掩膜&#xff08;Mask&#xff09; 2.2 调用cv2.inpaint()函数 2.3 完整代码示例 2.4 运行结果 …

快速建立私有化知识库(私有化训练DeepSeek,通过ollama方式)

简介 什么&#xff1f;&#xff01;老是有人问你需求&#xff0c;不同版本的需求你记不清还得去扒拉过程文档、设计文档&#xff1f; 什么&#xff1f;&#xff01;领导会询问功能使用情况、用户相关数据&#xff0c;你每次还得手动查询反馈&#xff1f; 什么&#xff1f;&…

python脚本实现windows电脑内存监控内存清理(类似rammap清空工作集功能)

import ctypes import psutil import time import sys import os from datetime import datetime import pyautogui# 检查管理员权限 def is_admin():try:return ctypes.windll.shell32.IsUserAnAdmin()except:return False# 内存清理核心功能 def cleanup_memory(aggressivene…

网络安全:挑战、技术与未来发展

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 在数字化时代&#xff0c;网络安全已成为全球关注的焦点。随着互联网的普及和信息技术的高速发展&#xff0c;网络攻击的…

Verilog语言学习总结

Verilog语言学习&#xff01; 目录 文章目录 前言 一、Verilog语言是什么&#xff1f; 1.1 Verilog简介 1.2 Verilog 和 C 的区别 1.3 Verilog 学习 二、Verilog基础知识 2.1 Verilog 的逻辑值 2.2 数字进制 2.3 Verilog标识符 2.4 Verilog 的数据类型 2.4.1 寄存器类型 2.4.2 …

35.Word:公积金管理中心文员小谢【37】

目录 Word1.docx ​ Word2.docx Word2.docx ​ 注意本套题还是与上一套存在不同之处 Word1.docx 布局样式的应用设计页眉页脚位置在水平/垂直方向上均相对于外边距居中排列&#xff1a;格式→大小对话框→位置→水平/垂直 按下表所列要求将原文中的手动纯文本编号分别替换…

Python----Python高级(并发编程:协程Coroutines,事件循环,Task对象,协程间通信,协程同步,将协程分布到线程池/进程池中)

一、协程 1.1、协程 协程&#xff0c;Coroutines&#xff0c;也叫作纤程(Fiber) 协程&#xff0c;全称是“协同程序”&#xff0c;用来实现任务协作。是一种在线程中&#xff0c;比线程更加轻量级的存在&#xff0c;由程序员自己写程序来管理。 当出现IO阻塞时&#xff0c;…