日撸Java三百行(day13:链表)

目录

一、链表的基础知识

二、链表的代码实现

1.链表创建

2.链表遍历

3.链表定位查找

4.链表插入

5.链表删除

6.数据测试

7.完整的程序代码

总结


一、链表的基础知识

在之前顺序表的学习中,我们其实提到过链表。链表它是线性表在不同的物理存储方式下派生出来的,链表不像顺序表,它的数据元素在物理存储结构上并不连续,其数据元素的逻辑顺序是通过链表中的引用链接次序实现的,总结一下就是链表在逻辑上连续,在物理上不连续。

链表中的每一个元素称为结点,因此链表其实是由一系列结点组成的。而每个结点包括两个部分:一个是存储数据元素的数据域data,另一部分则是存储下一个结点地址的指针域next,通过这个指针域我们就可以轻松访问到当前结点的下一个结点,直到某个结点的next域为null。这样,就相当于把所有的结点都串联起来,进行链式访问,所以叫做“链表”。图解如下:

在实际情况中,链表可以分为:

  • 单向or双向

  • 循环or非循环

  • 带头or不带头

组合起来,一共就有8种链表。 

二、链表的代码实现

与顺序表一样,链表也可以进行初始化、查找、插入、删除等操作,下面我们通过java来实现。

1.链表创建

在day11(顺序表一)中,我们学习了类和对象的相关知识,包括如何定义一个类、如何创建对象、如何使用对象等,基于此,我们在定义的链表类中再创建一个结点类,同时定义该结点类的成员变量(data、next)和成员方法,如下:

public class LinkedList {/*** An inner class.*/class Node {/*** The data.*/int data;/*** The reference to the next node.*/Node next;/********************* * The constructor* * @param paraValue*            The data.******************* */public Node(int paraValue) {data = paraValue;next = null;}// Of the constructor}// Of class Node

在上述代码中,我们有两个需要注意的问题:

  • 这里的结点类是一个内部类inner class,根据链表的基本概念,我们知道链表是由若干个结点组成,而结点又由data域和next域组成,所以其符合内部类的定义(如果外部类是由若干完整的类组成,那么这些若干完整的类就可以定义为内部类)
  • 在定义成员变量next时,使用的是Node引用,这是因为next所指向的是下一个结点的地址,而结点始终是Node类型,所以我们用Node来定义next

然后,我们再定义一个头结点,并通过new为其分配内存空间,代码如下:

    /*** The header node. The data is never used.*/Node header;/************************ Construct an empty linked list.**********************/public LinkedList() {header = new Node(0);//header.next = null; //Redundant}// Of the first constructor

为什么这里要单独把头结点header拿出来定义呢?一方面是因为header是链表的特征,而不是结点的;另一方面是因为头结点的数据域data是无效的,不存放任何数据元素。

2.链表遍历

与顺序表一样,我们同样可以重写toString()方法来遍历输出链表,如下:

    /************************ Overrides the method claimed in Object, the superclass of any class.**********************/public String toString() {String resultString = "";if (header.next == null) {return "empty";} // Of ifNode tempNode = header.next;while (tempNode != null) {resultString += tempNode.data + ", ";tempNode = tempNode.next;} // Of whilereturn resultString;}// Of toString

根据之前学过的如何使用一个对象,可以知道if语句中的header.next(对象名.成员变量)即表示头结点后的下一个结点的地址,如果其等于null,那么该链表显然为空,所以我们需要提前排除这种情况;排除后,我们将头结点后的下一个结点定义为tempNode(结点始终是Node类型,所以用Node定义);再通过while循环进行迭代,其中tempNode.data(对象名.成员变量)表示该结点所存放的数据,而tempNode = tempNode.next;则实现了对象的迭代,相当于由指向前一个结点移动为指向下一个结点,即可访问下一个结点的相关数据,这其实与数组中索引值递增的作用有点类似;最后通过循环条件tempNode != null来控制,当tempNode = null即到达最后一个结点时结束。

3.链表定位查找

其实链表定位查找方法创建的大体思路和顺序表没有什么差别,都是通过循环遍历,将现有的数据元素与目标元素一一比较,最大的不同就是遍历方式发生了改变,代码如下:

    /************************ Locate the given value. If it appears in multiple positions, simply* return the first one.* * @param paraValue*            The given value.* @return The position. -1 for not found.**********************/public int locate(int paraValue) {int tempPosition = -1;Node tempNode = header.next;int tempCurrentPosition = 0;while (tempNode != null) {if (tempNode.data == paraValue) {tempPosition = tempCurrentPosition;break;} // Of iftempNode = tempNode.next;tempCurrentPosition++;} // Of whilereturn tempPosition;}// Of locate
  • 第一步,我们同样设置一个非法值-1,它的作用就是当在链表中没有找到目标元素时(包括链表为空、链表不为空但遍历完后没有找到目标元素这两种情况),返回非法值-1
  • 第二步,定义头结点后的下一个结点,并定义此结点的下标为0(因为前面说过头结点的data域不存放数据,所以头结点后的下一个结点相当于是第一个有效的结点,所以将其下标定为0)
  • 第三步,利用while循环进行遍历,将链表中的数据元素tempNode.data与目标元素paraValue一一比对:如果相等就将此时数据元素的下标赋给tempPosition,然后通过break语句跳出循环,再返回tempPosition的值;如果不等就通过tempNode = tempNode.next进行迭代,继续遍历

4.链表插入

由于链表在物理存储结构上不一定连续,所以它不用像顺序表那样通过大段元素移动覆盖的方式间接完成插入、删除等操作。链表的插入只需要创建一个新的结点,然后修改该新结点插入位置前后的引用链接次序即可,图解如下(图源网络):

根据图示,要完成链表插入,大体上可以分为三步:

  • 第一步,创建一个新的结点
  • 第二步,找到指定插入位置的前一个结点
  • 第三步,修改该指定插入位置前后的指针指向

代码如下: 

    /************************ Insert a value to a position. If the list is already full, do nothing.* * @param paraPosition*            The given position.* @param paraValue*            The given value.* @return Success or not.**********************/public boolean insert(int paraPosition, int paraValue) {Node tempNode = header;Node tempNewNode;for (int i = 0; i < paraPosition; i++) {if (tempNode.next == null) {System.out.println("The position " + paraPosition + " is illegal.");return false;} // Of iftempNode = tempNode.next;} // Of for i// Construct a new node.tempNewNode = new Node(paraValue);// Now link them.tempNewNode.next = tempNode.next;tempNode.next = tempNewNode;return true;}// Of insert

第一步没什么好说的,直接创建即可;第二步中,我们需要提前考虑指定插入位置是否合法,这里将tempNode.next == null是否成立作为判断条件;确定插入位置合法之后,我们通过tempNode = tempNode.next;进行迭代,再利用i < paraPosition控制循环,使得到达指定插入位置的前一个结点;而第三步的修改链接次序,参考上图的解释就好。

5.链表删除

和链表插入同样的思想,链表删除也是通过修改链接次序来实现,不过链表删除还要更简单一点,只需要删掉目标位置前后的链接,再增加目标位置前一个结点与目标位置后一个结点之间的链接即可,图解如下(图源网络):

由于链表删除不需要创建新的结点,所以只需要两步:

  • 第一步,找到目标位置的前一个结点
  • 第二步,修改目标位置前后的链接次序

代码如下:

    /************************ Delete a value at a position.* * @param paraPosition*            The given position.* @return Success or not.**********************/public boolean delete(int paraPosition) {if (header.next == null) {System.out.println("Cannot delete element from an empty list.");return false;} // Of ifNode tempNode = header;for (int i = 0; i < paraPosition; i++) {if (tempNode.next.next == null) {System.out.println("The position " + paraPosition + " is illegal.");return false;} // Of iftempNode = tempNode.next;} // Of for itempNode.next = tempNode.next.next;return true;}// Of delete

在执行第一步之前,我们需要考虑两个问题,第一个问题是链表是否为空,第二个问题是目标位置是否合法。对于第一个问题,直接用一个if语句进行判断即可,当header.next = null时,头节点的后一个节点的地址为null,也就是第一个有效结点的地址为null,此时链表显然为空,所以返回false;而第二个问题,需要注意与链表插入不一样,此时的判断条件不再是tempNode.next == null,而是tempNode.next.next == null。解决以上两个问题后,就可以利用tempNode = tempNode.next;进行遍历,直到找到目标位置的前一个结点。

对于第二步的修改链接次序,只需要令tempNode.next = tempNode.next.next;即可,这里的temp.next.next其实就是目标位置后一个结点的地址,将该地址赋给tempNode.next(即目标位置前一个结点的next域),这样就把目标位置前一个结点与目标位置后一个结点链接起来了,也就实现了链表删除操作。

6.数据测试

方法创建完成后,我们来进行一些数据测试,如下:

    /************************ The entrance of the program.* * @param args Not used now.**********************/public static void main(String args[]) {LinkedList tempFirstList = new LinkedList();System.out.println("Initialized, the list is: " + tempFirstList.toString());for (int i = 0; i < 5; i++) {tempFirstList.insert(0, i);} // Of for iSystem.out.println("Inserted, the list is: " + tempFirstList.toString());tempFirstList.insert(6, 9);tempFirstList.delete(4);tempFirstList.delete(2);System.out.println("Deleted, the list is: " + tempFirstList.toString());tempFirstList.delete(0);System.out.println("Deleted, the list is: " + tempFirstList.toString());for (int i = 0; i < 5; i++) {tempFirstList.delete(0);System.out.println("Looped delete, the list is: " + tempFirstList.toString());} // Of for i}// Of main

7.完整的程序代码

package datastructure.list;/*** Linked list.* * @author Xin Lin 3101540094@qq.com.*/public class LinkedList {/*** An inner class.*/class Node {/*** The data.*/int data;/*** The reference to the next node.*/Node next;/********************* * The constructor* * @param paraValue*            The data.******************* */public Node(int paraValue) {data = paraValue;next = null;}// Of the constructor}// Of class Node/*** The header node. The data is never used.*/Node header;/************************ Construct an empty linked list.**********************/public LinkedList() {header = new Node(0);//header.next = null; //Redundant}// Of the first constructor/************************ Overrides the method claimed in Object, the superclass of any class.**********************/public String toString() {String resultString = "";if (header.next == null) {return "empty";} // Of ifNode tempNode = header.next;while (tempNode != null) {resultString += tempNode.data + ", ";tempNode = tempNode.next;} // Of whilereturn resultString;}// Of toString/************************ Reset to empty. Free the space through garbage collection.**********************/public void reset() {header.next = null;}// Of reset/************************ Locate the given value. If it appears in multiple positions, simply* return the first one.* * @param paraValue*            The given value.* @return The position. -1 for not found.**********************/public int locate(int paraValue) {int tempPosition = -1;Node tempNode = header.next;int tempCurrentPosition = 0;while (tempNode != null) {if (tempNode.data == paraValue) {tempPosition = tempCurrentPosition;break;} // Of iftempNode = tempNode.next;tempCurrentPosition++;} // Of whilereturn tempPosition;}// Of locate/************************ Insert a value to a position. If the list is already full, do nothing.* * @param paraPosition*            The given position.* @param paraValue*            The given value.* @return Success or not.**********************/public boolean insert(int paraPosition, int paraValue) {Node tempNode = header;Node tempNewNode;for (int i = 0; i < paraPosition; i++) {if (tempNode.next == null) {System.out.println("The position " + paraPosition + " is illegal.");return false;} // Of iftempNode = tempNode.next;} // Of for i// Construct a new node.tempNewNode = new Node(paraValue);// Now link them.tempNewNode.next = tempNode.next;tempNode.next = tempNewNode;return true;}// Of insert/************************ Delete a value at a position.* * @param paraPosition*            The given position.* @return Success or not.**********************/public boolean delete(int paraPosition) {if (header.next == null) {System.out.println("Cannot delete element from an empty list.");return false;} // Of ifNode tempNode = header;for (int i = 0; i < paraPosition; i++) {if (tempNode.next.next == null) {System.out.println("The position " + paraPosition + " is illegal.");return false;} // Of iftempNode = tempNode.next;} // Of for itempNode.next = tempNode.next.next;return true;}// Of delete/************************ The entrance of the program.* * @param args Not used now.**********************/public static void main(String args[]) {LinkedList tempFirstList = new LinkedList();System.out.println("Initialized, the list is: " + tempFirstList.toString());for (int i = 0; i < 5; i++) {tempFirstList.insert(0, i);} // Of for iSystem.out.println("Inserted, the list is: " + tempFirstList.toString());tempFirstList.insert(6, 9);tempFirstList.delete(4);tempFirstList.delete(2);System.out.println("Deleted, the list is: " + tempFirstList.toString());tempFirstList.delete(0);System.out.println("Deleted, the list is: " + tempFirstList.toString());for (int i = 0; i < 5; i++) {tempFirstList.delete(0);System.out.println("Looped delete, the list is: " + tempFirstList.toString());} // Of for i}// Of main
}// Of class LinkedList

运行结果:

总结

昨天我们讨论了顺序表,由于其底层是一段连续的物理空间,所以当想要在顺序表中的任意位置插入或删除元素时,必须通过大段元素整体后移或前移才能完成,所以效率很低,时间复杂度为O(n),针对这一问题,java引入了链表结构。链表有一个非常好的性质就是在插入、删除元素时不用移动元素,只改变引用即可,所以效率比较高。但是链表也有不足的地方,就是当需要定位查找元素时,链表只能按链接顺序依次访问数据元素,时间复杂度为O(n)。

综上所述,顺序表的优势是支持随机访问,并且查找元素比较快,而当需要频繁进行插入、删除元素操作时,链表会更为适合。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷歌账号被停用后,申诉没有反馈或者被拒绝后怎么办?附:谷歌账号申诉信要点和模板

有一些朋友在登录谷歌账号的时候&#xff0c;或者在是用谷歌账号的过程中突然被强制退出来&#xff0c;然后再次登录的时候就遇到了下面的提醒&#xff1a;您的账号已停用&#xff0c;而且原因通常是两大类&#xff1a;1&#xff09;谷歌账号与其他多个账号一起创建或使用的&am…

将网络变压器(Ethernet Transformer)从千兆单口设计改为百兆双口设计涉及几个关键步骤和注意事项

变压器选型&#xff1a; 确保选用的变压器支持1000BASE-T到100BASE-TX的转换。通常&#xff0c;这种变压器会有额外的电气特性&#xff0c;如抑制和隔离等&#xff0c;以确保数据传输的可靠性和稳定性。 端口连接&#xff1a; 对于千兆单口设计&#xff0c;通常会有一对输入和输…

【python报错已解决】`AttributeError: ‘DataFrame‘ object has no attribute ‘ix‘`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言&#xff1a; 在使用Pandas库进行数据分析时&#xff0c;你是否遇到过AttributeError: DataFrame object has no attribut…

本地安装Llama3.1与LobeChat可视化UI界面并实现远程访问大模型实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

企业定制AI智能名片微信小程序在私域流量运营中的应用与策略

摘要&#xff1a;随着2022年私域运营市场步入冷静期&#xff0c;企业逐渐从盲目模仿向精准化、个性化的运营模式转变。在这一背景下&#xff0c;企业定制AI智能名片微信小程序凭借其独特的智能化、便捷化特性&#xff0c;成为企业构建私域流量池、深化用户关系、实现高效转化的…

【完美解决】正点原子Linux开发板无法联网ping通百度但可以ping通主机和虚拟机,联通了局域网但无法联通互联网,DNS配置问题

问题记录 主机通过共享网络给以太网口想让正点原子的阿尔法Linux开发板连上网&#xff0c;网络配置过程如下&#xff1a; 开发板连接的是eth1口&#xff0c;通过在终端输入以下命令进入网络配置文件。 vi /etc/network/interfaces 将其配置为了以下地址 但是出现了一些问题&…

RAG 入门指南:从零开始构建一个 RAG 系统

本文正文字数约 3300 字&#xff0c;阅读时间 10 分钟。 从零开始构建一个应用可以让我们快速理解应用的各个部分。 这个方法其实非常适用于 RAG。 我在以前的文章中有介绍过 RAG 的概念、原理以及应用等&#xff0c;但其实&#xff0c;亲自动手来构建一个 RAG 系统或许能够…

C语言指针详解(三)目录版

C语言指针详解&#xff08;三&#xff09;目录版 1、字符指针变量1.1、字符指针变量的一般应用1.2、常量字符串1.3、常量字符串与普通字符串的区别1.3.1 常量字符串的不可修改性1.3.2 常量字符串的存储 2、数组指针变量2.1、数组指针变量定义2.2、数组指针变量的初始化 3、二维…

图的DFS

LeetCode2368 受限条件下可到达节点的数目 class Solution { public:int dfs(vector<vector<int>>& g,int x,int fa){int sum1;for(int y:g[x]){if(y!fa) sumdfs(g,y,x);}return sum;}int reachableNodes(int n, vector<vector<int>>& edges, …

C语言指针(3)

目录 一、字符指针变量 二、数组指针变量 三、⼆维数组传参的本质 四、函数指针变量 五、typedef 关键字 六、函数指针数组 一、字符指针变量 字符指针char* &符号名 符号名&#xff0c;这都是获取的是首元素地址。 int main() {char a[] "abcdef";cha…

另一棵树的子树 - 力扣(LeetCode)C语言

572. 另一棵树的子树 - 力扣&#xff08;LeetCode&#xff09;&#xff08;点击前面链接即可查看题目&#xff09; 一、题目 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&…

机器学习中的关键距离度量及其应用

引言 在当今的数据驱动世界中&#xff0c;机器学习算法扮演着至关重要的角色&#xff0c;它们在图像分类、面部识别、在线内容审核、零售目录优化和推荐系统等多个领域发挥着重要作用。这些算法的核心在于它们能够识别和利用数据之间的相似性。而实现这一点的关键&#xff0c;…

ShardingSphere 内核工作原理

文章目录 内核工作原理配置管控SQL Parser: SQL解析引擎SQL Router- SQL 路由引擎SQL Rewriter : SQL 优化引擎SQL Executor &#xff1a; SQL执行引擎Result Merger&#xff1a; 结果归并 内核工作原理 ShardingSphere的整体架构图是这样的&#xff1a; 配置管控 在进入Shar…

MySQL事务,锁,MVCC总结

mysql中最重要的就是事务&#xff0c;其四大特性让我们维持了数据的平衡&#xff0c;一致。那么事务究竟是什么&#xff0c;与什么相关&#xff0c;他的使用步骤&#xff0c;以及使用过程中我们会遇到什么问题呢&#xff1f;下面我们一起学习交流! 1.MySQL的存储引擎&#xff…

SPIFFS与LittleFS的对gz文件格式的区别

SPIFFS 只能安装在Arduino上。LittleFS支持Arduino IDE和VScode的 PlatformIO。 SPIFFS serveStatic: server.serveStatic("/", SPIFFS, "/") 负责提供 SPIFFS 文件系统中的文件。您可以在 SPIFFS 上放置 .gz 文件&#xff0c;并该方法将自动处理它们。 …

git cz代码提交规范,适用于node14以上

1.效果 3. 在项目中如何添加 3.1 安装(只提供npm安装方式)全局安装 commitizen npm i -D commitlint commitlint/config-conventional commitizen cz-git 3.2 配置模版 在项目的根目录下配置添加文件 commitlint.config.js 并写入如下代码 /** type {import(cz-git).UserCo…

C# 类型转换

隐式&#xff08;implicit&#xff09;类型转换 1.不丢失精度的转换 2.显示&#xff08;explicit&#xff09;类型的转换 有可能丢失精度的转换 使用convert转换 ToString方法&#xff1a;将数值类型转换成字符串型

PDF密码移除技巧: 五大 PDF 密码移除器

如果您想解密或删除 PDF 密码&#xff0c;该怎么办&#xff1f;PDF 是一种经常用于商业的格式&#xff0c;您可以在培训、教育和商业场合使用它。添加这些 PDF 文件的密码可以保护您的安全和隐私。因此&#xff0c;有很多 PDF 都用密码加密&#xff0c;当您想要查看这些 PDF 时…

吃透张宇1000题和660题,能保底100分吗?

暑假已经过一半了&#xff0c;很多人都在埋头做题&#xff0c;如果你选择的是1000题660题 一定要好好看这篇笔记&#xff01; 因为很多人做题做到现在&#xff0c;有点迷茫 主要的迷茫点有三个&#xff1a; 1、为什么1000题和660题也都做不少了&#xff0c;遇到新题&#x…

RS485 芯片SN65HVD72DR导致的死机问题调试

最近再一次栽倒在这颗RS485 芯片上了&#xff0c;硬件说这和芯片功耗有点高&#xff0c;要控下电源, 结果10次有9次程序死机&#xff01; 先上图&#xff0c;请各位高手看看&#xff0c;这电路有问题没有&#xff1f; 为什么我会说是RS485 芯片导致的死机&#xff1f;因为 只要…