【数据结构】单向链表的创建及4种应用

目录

前言

自定义“单向”链表类

1. 自定义一个链表类,并完成“初始化链表”、“添加元素(头插法/尾插法)”、“计算链表长度”操作;

自定义链表

向链表中插入元素(头插法)

向链表中插入元素(尾插法)

 计算链表长度

 重写toString()方法

测试类

测试结果

对链表的应用

2. 在自定义链表类的基础上,使用“双指针”实现两个链表的合并;

测试用例

 测试结果

3. 在自定义链表类的基础上,通过“栈”反转链表;

测试用例

测试结果

 4. 在自定义链表类的基础上,计算两个大型整数(BigInteger)和;

 测试用例

测试结果

5.1 在自定义链表类的基础上,使用"Set"集合 测试链表中是否存在环路现象;

测试用例

测试结果

5.2 在自定义链表类的基础上,基于"快慢指针"判断链表中是否存在环路;

测试用例

测试结果


前言

相关传送门:===》【算法】链表手撕代码《===

链表的特点:

  • 链表全称“链式存储结构”,属于线性表的一种;
  • 链表由“数据域”和“指针域”这两部分组成;
  • 在链表中,“实际存储”的是一个个“节点”,在物理上是非连续的数据结构;

链表的优点:

  • 不需要提前预估长度,较于“数组”不存在“扩容操作”
  • 使用不连续的内存空间,实现灵活的内存动态管理

链表的缺点:

  • 较于数组会占用更多空间,因为其需要存放其他节点的指针;
  • 不支持随机读取元素(RandomAccess) —— 数组类实现类一个标准类 RandomAccess表示支持随机读取;
  • 遍历和查找元素较慢,适合读少写多的操作;

链表的时间复杂度:

  • 插入和删除操作的复杂度为 O(1);
  • 查找或访问某一特定节点复杂度为 O(n);

链表的分类:

  1. 单向链表
  2. 双向链表
  3. 循环链表
  4. 双向循环链表

自定义“单向”链表类

1. 自定义一个链表类,并完成“初始化链表”、“添加元素(头插法/尾插法)”、“计算链表长度”操作;

  • 自定义链表

public class Linked{//定义头节点和尾节点private Node first,last;//链表长度private int size;//定义链表的节点static class Node{int val;  //数据域Node next; //后继元素(下一个元素)public Node(int x){this.val =x;}}
}

解读:

  • 定义了私有成员变量 first 和 last ,分别代表链表的头节点和尾节点;
  • 通过 size 变量记录链表的长度;
  • 在自定义 Linked 类中定义一个静态内部类 Node ,用于表示链表的节点;
  • 每个 Node 对象包含两个部分:int 类型的 val 变量用于存储节点的值,以及指向下一个节点的引用 next;
  • 向链表中插入元素(头插法)

public class Linked{// 添加元素(头插法)public void addFirst(int val) {// 创建新节点final Node newNode = new Node(val);// 将新节点的 next 指向当前的第一个节点newNode.next = first;// 更新头节点为新节点first = newNode;// 如果链表为空,则更新尾节点为新节点if (last == null) {last = newNode;}// 增加链表长度size++;}}

解读:

  • 创建一个新节点 newNode,值为传入的参数 val;
  • 将新节点的 next 指向当前的第一个节点 first ,把当前的头节点作为新节点的后继节点;
  • 更新链表的头节点为新节点;
  • 如果链表为空(即尾节点为 null ),则将尾节点也更新为新节点;
  • 最后增加链表长度 size;
  • 向链表中插入元素(尾插法)

public class Linked{//添加元素(尾插法)public void add(int val){//获取当前链表的尾节点final Node l = last ;//创建新节点final Node newNode = new Node(val);if(l !=null){//链表不为空l.next = newNode;}else{//链表为空first = newNode;}last = newNode;size++;}
}

解读:

  • 获取当前链表的尾节点 last;
  • 创建一个新的节点 newNode,值为传入的参数 val;
  • 如果尾节点不为空,则将尾节点的 next 指向新节点;如果为空,则将头节点指向新节点;
  • 更新尾节点为新节点,同时增加链表长度 size;
  •  计算链表长度
public class Linked{//返回链表长度public int size(){return size;}
}

解读:

  • 返回链表的长度,即 size 变量的值;
  •  重写toString()方法
public class Linked{public String toString(){//使用线程不安全,但性能较好的StringBuilderStringBuilder ret = new StringBuilder();//遍历链表for(Node x =first;x !=null;x = x.next){ret.append(x.val);if(x.next !=null){ret.append("->");}}return ret.toString();}
}

解读:

  • 使用 StringBuilder 构建字符串,遍历链表中的每个节点,将节点的值追加到字符串中;
  • 如果节点有下一个节点,则在值之后添加"->"表示连接关系;
  • 最后返回拼接好的字符串表示链表内容;
  • 测试类
public class LinkedTest {public static void main(String[] args) {//初始化链表1Linked linked1 = new Linked();linked1.add(1);linked1.add(3);linked1.add(5);linked1.add(7);linked1.addFirst(9);System.out.println(linked1.size());System.out.println(linked1.toString());}
}
  • 测试结果


对链表的应用

2. 在自定义链表类的基础上,使用“双指针”实现两个链表的合并;

    //双指针合并两个单向链表public static Linked meger(Linked l1 ,Linked l2 ){//定义双指针,分别执行两个有序链表的头节点Node p1 =l1.first, p2 = l2.first;//用于保存合并结果的链表Linked resultLinked = new Linked();while(p1 !=null || p2 !=null){if(p1 == null){resultLinked.add(p2.val);p2 = p2.next;continue;}if(p2 == null){resultLinked.add(p1.val);p1 = p1.next;continue;}//比较两个节点if(p1.val < p2.val){resultLinked.add(p1.val);p1 = p1.next;}else{resultLinked.add(p2.val);p2 = p2.next;}}return resultLinked;}

解读:

  • 接收两个参数 l1 和 l2,分别表示要合并的两个链表;
  • 在方法内部定义两个指针 p1 和 p2,分别指向两个链表的头节点;
  • 定义一个 Linked 对象 resultLinked 用于保存合并结果;
  • 进入循环后会判断两个指针是否都指向 null,只要其中一个指针不为空就继续执行;
  • 如果 p1 或 p2 任一个为 null,则直接将另一个链表剩余部分添加到 resultLinked 中,并移动指针到下一个节点;
  • 如果两个节点都不为 null,则比较两个节点的值:如果 p1 的值小于 p2 的值,则将 p1 的值添加到 resultLinked 中,并将 p1 指针指向下一个节点;
  • 如果 p2 的值小于等于 p1 的值,则将 p2 的值添加到 resultLinked 中,并将 p2 指针指向下一个节点;
  • 循环结束后,返回合并后的链表 resultLinked;

时间复杂度为 O(m+n),其中 m 和 n 分别表示两个链表的长度;

  • 测试用例
        Linked linked2 =new Linked();linked2.add(2);linked2.add(4);linked2.add(6);linked2.add(8);linked2.add(10);//链表合并Linked megerRet = Linked.meger(linked1, linked2);System.out.println("合并结果"+megerRet);
  •  测试结果


3. 在自定义链表类的基础上,通过“栈”反转链表;

    public static Linked reverseLinked(Linked linked){Stack<Node> nodeStack = new Stack<>();//遍历链表,将所有Node节点,存入栈中Node currentNode = linked.first;if(currentNode ==null){return linked;}while (currentNode !=null){nodeStack.push(currentNode);currentNode =currentNode.next;}//遍历栈,将栈中的Node节点,从栈顶依次出栈(逆序),并存入链表中linked = new Linked(); //清空原链表while (!nodeStack.empty()){linked.add(nodeStack.pop().val);}return linked;}

解读:

  • 接收一个参数 linked,表示要逆序的链表;
  • 创建一个 Stack 对象 nodeStack 用于存储链表的节点;
  • 获取链表的头节点,并将其赋值给 currentNode;
  • 如果链表为空,直接返回原链表,否则,通过循环遍历链表,将每个节点依次压入栈中,并将 currentNode 指针移动到下一个节点;
  • 创建一个新的空链表对象 linked,用于存放逆序后的节点;
  • 通过循环遍历栈中的节点,每次从栈顶取出一个节点,并将其值添加到 linked 链表中(此时节点的顺序已经逆序);
  • 循环结束后,返回逆序后的链表 linked;

时间复杂度为 O(n),其中 n 表示链表的长度;

  • 测试用例
        Linked linked1 = new Linked();linked1.add(1);linked1.add(3);linked1.add(5);linked1.add(7);linked1.add(9);linked1 = Linked.reverseLinked(linked1);System.out.println("链表反转的结果"+linked1);
  • 测试结果


 4. 在自定义链表类的基础上,计算两个大型整数(BigInteger)和;

    //用"链表"计算两个大型整数和public static Linked addTwoNumber(Linked l1 ,Linked l2){//从个位开始计算(链表的头部)Node n1 =l1.first;Node n2 =l2.first;Linked retLinked = new Linked();int carry = 0;//进位while ( n1 != null || n2 !=null){//分别获取当前计算的数字int x =n1 != null ? n1.val :0;int y =n2 != null ? n2.val :0;int sum =x + y + carry;//计算进位值carry = sum /10;//保存当前位计算结果retLinked.add(sum %10);if(n1 !=null){n1 =n1.next;}if(n2 !=null){n2 =n2.next;}}if(carry !=0){retLinked.add(carry);}retLinked= Linked.reverseLinked(retLinked);return retLinked;}

 解读:

  • 接收两个参数 l1 和 l2,分别表示要相加的两个链表;
  • n1 和 n2 分别表示链表 l1 和 l2 的头节点;
  • 通过将 l1 和 l2 的头节点赋值给 n1 和 n2,开始从个位开始计算两个链表的和;
  • 创建一个 Linked 对象 retLinked,用于存储计算结果;
  • carry 表示进位的值,初始值为 0;
  • 通过循环遍历链表中的节点,直到两个链表都遍历完;
  • 在循环中,首先判断当前节点是否为空,如果不为空,则获取当前节点的值;如果为空,则将当前节点的值置为 0;
  • 计算当前位的和(包括进位),并更新进位的值;
  • 将当前位的和除以 10 取余数,并将余数添加到 retLinked 链表中作为当前位的计算结果;
  • 如果链表节点不为空,则将当前节点指针移动到下一个节点;
  • 在循环结束后,如果进位不为 0,则将进位值添加到 retLinked 链表的末尾;
  • 最后,调用 Linked 类的静态方法 reverseLinked 将 retLinked 链表进行逆序操作;
  • 返回逆序后的链表 retLinked,即两个大型整数(BigInteger)的和;

从个位开始逐位计算两个大型整数的和,并将结果保存在一个新的链表中。时间复杂度为 O(max(n, m)),其中 n 和 m 分别表示链表 l1 和 l2 的长度;

  •  测试用例
        Linked linked3 = new Linked();linked3.add(3);linked3.add(4);linked3.add(2);Linked linked4 =new Linked();linked4.add(1);linked4.add(2);linked4.add(3);Linked linked = Linked.addTwoNumber(linked3, linked4);linked3 =Linked.reverseLinked(linked3);linked4 =Linked.reverseLinked(linked4);System.out.printf("两数字%s,%s计算和:%s\n",linked3.toString(),linked4.toString(),linked.toString2());
  • 测试结果


5.1 在自定义链表类的基础上,使用"Set"集合 测试链表中是否存在环路现象;

   public static boolean hasCycle1(Node node){HashSet<Node> nodeHashSet = new HashSet<>();while (node !=null){if(nodeHashSet.contains(node)){return true;}nodeHashSet.add(node); //保存至Setnode = node.next;}return false;}

解读:

  • 接收一个参数 node,表示链表的头节点;
  • 创建一个 HashSet 对象 nodeHashSet,用于存储遍历过的节点;
  • 通过循环遍历链表中的节点,直到遍历到链表末尾(node 为 null)或发现循环;
  • 在循环中,首先检查当前节点是否已经存在于 nodeHashSet 中,如果存在则说明链表中存在循环,直接返回 true;
  • 如果当前节点不在 nodeHashSet 中,则将当前节点添加到 nodeHashSet 中,并将指针移动到下一个节点;
  • 如果遍历完整个链表后都没有发现循环,则返回 false,表示链表中不存在循环;

时间复杂度为 O(n),其中 n 表示链表中节点的数量;

  • 测试用例
        //判断链表中是否存在环路Linked.Node node1 =new Linked.Node(1);Linked.Node node2 =new Linked.Node(2);Linked.Node node3 =new Linked.Node(3);Linked.Node node4 =new Linked.Node(4);Linked.Node node5 =new Linked.Node(5);node1.next =node2;node2.next =node3;node3.next =node4;node4.next =node5;for(Linked.Node x =node1;x!=null;x =x.next){System.out.print(x.val);if(x.next !=null) {System.out.print("=>");}}System.out.println("是否存在环路:"+Linked.hasCycle1(node1));
  • 测试结果

5.2 在自定义链表类的基础上,基于"快慢指针"判断链表中是否存在环路;

   public static  boolean hasCycle2(Node head){//定义快慢指针Node fast = head;Node slow = head;while (fast != null && fast.next != null && slow !=null){fast =fast.next.next; //快指针每次移动2步slow =slow.next; //慢指针每次移动1步if(fast == slow){return true;}}return false; //链表无环}

解读:

  • 接收一个参数 head,表示链表的头节点;
  • 定义两个指针 fast 和 slow,初始时它们都指向链表的头节点 head;
  • 通过循环遍历链表中的节点,直到快指针移动到链表末尾(fast 或 fast.next 为 null);
  • 在循环中,快指针每次向前移动两步(fast = fast.next.next),慢指针每次向前移动一步(slow = slow.next);
  • 在每次移动后,判断快指针是否与慢指针相遇,如果相遇则说明链表中存在循环,直接返回 true;
  • 如果遍历完整个链表后快指针到达链表末尾仍未相遇,则返回 false,表示链表中不存在循环;

时间复杂度为 O(n),其中 n 表示链表中节点的数量。这种方法在空间复杂度上优于使用 HashSet 存储节点的方法;

  • 测试用例

        //判断链表中是否存在环路Linked.Node node1 =new Linked.Node(1);Linked.Node node2 =new Linked.Node(2);Linked.Node node3 =new Linked.Node(3);Linked.Node node4 =new Linked.Node(4);Linked.Node node5 =new Linked.Node(5);node1.next =node2;node2.next =node3;node3.next =node4;node4.next =node5;node5.next =node3; //存在环路System.out.println("是否存在环路:"+Linked.hasCycle2(node2));
  • 测试结果


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

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

相关文章

2024年AI辅助研发:技术革新引领研发新纪元

文章目录 &#x1f4d1;前言一、AI辅助研发的技术进展二、行业应用案例三、面临的挑战与机遇四、未来趋势预测全篇总结 &#x1f4d1;前言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已逐渐成为推动社会进步的重要力量。特别是在研发领域&#xff0c;A…

计算机视觉——目标检测(R-CNN、Fast R-CNN、Faster R-CNN )

前言、相关知识 1.闭集和开集 开集&#xff1a;识别训练集不存在的样本类别。闭集&#xff1a;识别训练集已知的样本类别。 2.多模态信息融合 文本和图像&#xff0c;文本的语义信息映射成词向量&#xff0c;形成词典&#xff0c;嵌入到n维空间。 图片内容信息提取特征&…

【北京大学】徐高《金融经济学二十五讲》

一、经济的任务 经济的任务之一是确保有效地分配稀缺资源&#xff0c;这是经济学中的一个核心问题。资源是有限的&#xff0c;而需求是无限的&#xff0c;因此经济系统需要通过合理的机制来分配资源以满足社会的需求。以下是关于经济分配资源的几个方面&#xff1a; 1. 资源配…

CentOS7.9 Nginx + EMQX集群组建 MQTTS平台

前面我们有介绍过单机版EMQX的安装 CentOS7 安装 EMQX&#xff08;MQTT&#xff09;&#xff0c;今天我们来讲一下实际项目里用的到MQTTS平台。 一、EMQX单机配置 简单部署两个节点&#xff0c;修改对应配置文件 (/usr/local/emqx/etc/emqx.conf) 中的node内容&#xff1a; nam…

HTML 学习笔记(十)块和内联

每个HTML元素都有一个默认的显示值&#xff0c;显示值又可以再分为block(块)和inline(内联) 一、块元素 通过F12进入浏览器开发者模式查看该元素会发现其所占宽度为整个网页的宽度 1.div标签 通过div标签将一些元素装进"盒子"&#xff0c;从而对盒子中的全部元素…

HDFS的架构优势与基本操作

目录 写在前面一、 HDFS概述1.1 HDFS简介1.2 HDFS优缺点1.2.1 优点1.2.2 缺点 1.3 HDFS组成架构1.4 HDFS文件块大小 二、HDFS的Shell操作&#xff08;开发重点&#xff09;2.1 基本语法2.2 命令大全2.3 常用命令实操2.3.1 上传2.3.2 下载2.3.3 HDFS直接操作 三、HDFS的API操作3…

提前十分钟!有方法论的人和没有方法论的人,谁更从容?弱者不应被错误引导——早读(逆天打工人爬取热门微信文章解读)

熬夜不熬夜&#xff0c;取决于你的生活态度 引言Python 代码第一篇 人民日报 提前十分钟&#xff0c;人生大不同第二篇 人民日报 来啦 新闻早班车要闻社会政策 结尾 君子如潜龙&#xff0c;藏器待时发 紧握时间的脉搏&#xff0c;提前规划十分钟 既显对他人的敬意&#xff0c;亦…

【SysBench】Linux 安装 sysbench-1.20

安装目的是为了对 MySQL 8.0.x 、PostgreSQL 进行基准测试。 0、sysbench 简介 sysbench 是一个可编写脚本的多线程基准测试工具&#xff0c;基于 LuaJIT 。 它最常用于数据库基准测试&#xff0c;但也可以 用于创建任意不涉及数据库服务器的复杂工作负载。 sysbench 附带以…

【诚信3·15】广州流辰信息|诚信至上,始终如一!

每一个承诺&#xff0c;广州流辰信息皆倾心对待&#xff1b;每一份期待&#xff0c;广州流辰信息亦用心守护。近十年用专业缔造好品质&#xff0c;用服务追求好口碑。在为客户服务的路上&#xff0c;流辰信息始终无惧考验&#xff0c;保持初心。在3.15国际消费者权益日&#xf…

SpringBoot3学习记录(有ssm基础)

目录 一、SpringBoot3 介绍 SpringBoot3 简介 SpringBoot3 快速入门 入门总结 1.为什么依赖不需要写版本 2.Startrer&#xff08;启动器&#xff09;是什么 3.SpringBootApplication 二、SpringBoot3 配置文件 统一配置管理 使用yaml配置文件&#xff08;推荐&#x…

Gateway网关在url参数带有特殊字符的情况下转发失败(响应400)

本文主要分享了&#xff0c;SpringCloud Gateway网关在url参数带有空格或者特殊字符的情况下&#xff0c;转发失败导致响应错误码400的解决方案。 响应400错误码的2种场景&#xff1a; 1.参数带空格&#xff0c;Gateway会误认为该空格是切割符&#xff0c;如?phone 135****6…

QT使用dumpcpp为COM生成h及cpp的方式,COM是C#的dll注册的

目录 1.C#的dll注册为COM&#xff0c;采用bat的方式 2.通过qt的dumpcpp来生成h及cpp文件 3.h文件和cpp文件处理。 台达数控系统的C#的dll dumpcpp用的tlb文件 dumpcpp生成的原生h文件 dumpcpp生成的原生cpp dump生成后的的原生cpp文件修改后的cpp文资源 dump生成后的的…

AI短视频矩阵系统介绍|罐头鱼AI视频批量生成

智能化管理&#xff0c;轻松批量剪辑短视频&#xff01; 近年来&#xff0c;随着短视频营销行业的发展&#xff0c;我们推出了一款AI短视频矩阵系统&#xff0c;旨在帮助用户管理、剪辑和发布短视频内容&#xff0c;从而提升品牌影响力。让我们来看看这款系统都提供了哪些功能&…

Windows Server 各版本搭建终端服务器实现远程访问(03~19)

一、Windows Server 2003 左下角开始➡管理工具➡管理您的服务器&#xff0c;点击添加或删除角色 点击下一步 勾选自定义&#xff0c;点击下一步 点击终端服务器&#xff0c;点击下一步 点击确定 重新登录后点击确定 点击开始➡管理工具➡计算机管理&#xff0c;展开本地用户…

openssl3.2 - 官方demo学习 - encode - ec_encode.c

文章目录 openssl3.2 - 官方demo学习 - encode - ec_encode.c概述笔记产生ecc私钥产生ecc公钥测试工程备注备注END openssl3.2 - 官方demo学习 - encode - ec_encode.c 概述 官方demos/encode 目录中给了2个例子工程 功能是载入(RSA/ECC)公钥, 然后自己就可以拿内存中的公钥对…

mupdf渲染过程(一):颜色

mupdf除了解析PDF功能之外&#xff0c;还有一个强大的功能就是渲染文字和图像&#xff0c;本文介绍mupdf渲染过程中涉及到的颜色问题&#xff1a;包括颜色空间&#xff0c;颜色转换&#xff0c;lcms的使用。 1.初始化 mupdf初始化第一步是实例化fz_context *ctx&#xff0c;fz…

OpenCV开发笔记(七十七):相机标定(二):通过棋盘标定计算相机内参矩阵矫正畸变摄像头图像

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/136616551 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子(红模仿)的博…

Visual Studio 2022 配置“Debug|x64”的 Designtime 生成失败。IntelliSense 可能不可用。

今天写代码&#xff0c;无缘无故就给我整个这个错误出来&#xff0c;我一头雾水。 经过我几个小时的奋战&#xff0c;终于解决问题 原因就是这个Q_INTERFACES(&#xff09;宏&#xff0c;我本想使用Q_DECLARE_INTERFACE Q_INTERFACES这两个Qt宏实现不继承QObject也能使用qobjec…

VSCode提交代码

VSCode提交代码方式&#xff1a; 先在电脑本地文件夹中打开git的bash窗口使用git clone https://github.com/xxxx/克隆仓库地址到本地&#xff0c;并生成一个项目的文件夹打开VSCode&#xff0c;点击文件按钮&#xff0c;打开加载项目的文件夹对于VSCode设置Git路径&#xff…

Three 材质纹理 (总结三)

THREE.MeshLambertMaterial&#xff08;网格 Lambert 材质&#xff09; 该材质使用基于非物理的Lambertian模型来计算反射率。可以用来创建暗淡的并不光亮的表面&#xff0c;该材质非常易用&#xff0c;而且会与场景中的光源产生反应。 MeshLambertMaterial属性 # .color : …