LinkedList与链表 和 链表面试题

一. ArrayList 与 LinkedList 的优缺点:

         ArrayList 在任意位置删除或插入元素时,就需要把后面的所有元素整体往前或者往后移动,时间复杂度为O(n),效率较低,但是适合需要频繁访问元素的情况。空间不够会涉及到自动扩容,容易浪费空间。

        

        LinkedList 链表 可以理解为 一个长长的火车,每一个 "车厢" 为一个对象元素,也叫做节点,每一个对象元素有两个或三个变量,两个变量的是 单向链表,三个变量的是双向链表,其中链表其中一个变量一般是存放数据,其余的变量存放的是其他对象的引用,可以通过这个对象引用访问其他 "车厢"也就是节点。  LinkedList 适合 需要频繁修改节点数据时使用。

        

        另外,ArrayList  和 LinkedList 在内存存储上也有不同。ArrayList 它在内存中存储是连续的,而 LinkedList 存储逻辑是连续的,但是在内存不一定连续,因为 LinkedList 可以通过 其节点其中的变量 来找到 下一个 节点。

        

二. LinkedList 的分类

        LinkedList 分为一下几种结构:

        1.单向 或者 双向 。

        2.带头 或者 不带头。

        3.循环 或者 非循环。

        

        其中 一 一 组合一共有八种结构,但是我们只需要掌握两种结构就行了:

        1. 无头单向非循环链表

        这种一般在面试笔试出现会比较多。

        结构如图:

        

         val 代表的是存储的数据,next 代表存储的是下一个节点的对象引用(可以理解为下一个节点的地址),可以通过 next 找到下一个节点 。  

        

        

        2.无头双向链表

        在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

        结构如图:

        

           val 代表的是存储的数据,next 和 prev 分别代表存储的是 下一个节点 和上一个节点 的对象引用(可以理解为下一个节点的地址),可以通过 next 找到下一个节点 。也可以通过 prev 找到上一个节点。

   

  

   三.链表的十道面试题:

         1. 删除链表中等于给定值 val 的所有节点。题目链接

         题解思路

        1.首先,要判断传过来的链表是否为空,若为空则返回 null ,否则进入下一步。

       2. 其次,定义两个节点指向,一个在前,一个在后。

        若后面的节点存储的值为val,则要对节点重链接,然后后节点往前走一步,前节点不动。

        如果后面的节点存储的值不为val,则前节点走一步,后节点走一步。

        3.最后,因为上述的过程 无法判断 最开始节点的 值,所以再判断 最开始节点的值。

        题解代码

if(head == null) {return null;}ListNode cur = head.next;ListNode curL = head;while( cur != null) {if(cur.val == val) {curL.next = cur.next;}else {curL = cur;;}cur = cur.next;}if(head.val == val) {head = head.next;}return head;

        2. 反转⼀个单链表。题目链接

        题解代码

if(head == null) {return null;}if(head.next == null) {return head;}ListNode cur = head;ListNode curN = head.next;head.next = null;while(curN != null) {ListNode curNN = curN.next;curN.next = cur;cur =  curN;curN = curNN;}return cur;

        题解思路

        1.首先,判断是否为空链表或者只有一个节点的链表的情况。

        2.定义两个指向 cur 和 curN ,一个在前,一个在后,再把头节点置为null

        3.在循环里定义个 curNN ,记录之后节点反转两个节点后 curN 所要在的地方。

        注意:循环里不用 cur.next != null 作为条件,是因为cur.next当前所指向的是前一个节点,那么cur.next是访问前一个节点了。这也是什么要定义 curNN 的原因。

        4.最后,返回完成后的链表的头节点。

        

        3.  输⼊⼀个链表,输出该链表中倒数第k个结点。题目链接

        题解代码

ListNode fast = head;ListNode slow = head;int count = k-1;while(count != 0) {fast = fast.next;count--;}while(fast.next != null) {fast = fast.next;slow = slow.next;}return slow.val;

        题解思路

        1.首先,定义两个指向 fast 和 slow

        2.因为返回的是倒数第几个节点的值,那么可以通过让fast 先走 k-1步,再让slow 和 fast 一起走直到 fast 走到最后一个节点为止。那么slow 少走的几步就是 当前 的所需节点。

        

        4.给定⼀个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。题目链接

        题解代码

if(head == null) {return null;}if(head.next == null) {return head;}ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}return slow;

        题解思路

        1.首先,判断链表是否为空,或者只有一个节点。

        2.定义一个快指针,一个慢指针。

        3.每次循环让快指针走两步,慢指针走一步,当快指针遇到链表结尾,当前的慢指针就是中间节点。特别要注意循环条件

        

        5.将两个有序链表合并为⼀个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。题目链接

        题解代码

 if(list1 == null && list2 == null) {return null;}ListNode curA = list1;ListNode curB = list2;ListNode curN = new ListNode(1);ListNode cur = curN;while(curA != null && curB != null) {if(curA.val < curB.val) {cur.next = curA;cur = curA;curA = curA.next;}else {cur.next = curB;cur = curB;curB = curB.next;}}if(curA == null) {cur.next = curB;}if(curB == null) {cur.next = curA;}return curN.next;

        题解思路

        1.首先,判断两个链表是否为空。

        2.分别给两个不同的链表定义一个头节点,curA 和 curB ,再定义一个 “傀儡” 节点curN,最后再定义一个 节点 cur 从 “傀儡” 节点开始,cur用来帮助我们重新建立链表的大小链接。

        3.比较 curA 和 curB 的大小,小的节点 被作为 cur 重新建立顺序的一方,再让 cur 走到 小节点的位置,小节点再往后走一步,直到某个链表走到尾部了。

        4.最后,把走到尾部的一方链接上 没走到尾部链表的curA 或者 curB 位置 。最后返回 “傀儡” 节点后一个节点作为新链表的起始位置。

        

        6.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。题目链接

        题解代码

ListNode bs = null;ListNode be = null;ListNode as = null;ListNode ae = null;ListNode cur = pHead;while(cur != null) {if(cur.val < x) {//第一次if(bs == null) {bs = cur;be = cur; }else {be.next = cur;be = cur;}}else {//第一次if(as == null) {as = ae = cur;}else {ae.next = cur;ae = cur;}}cur = cur.next;}if(bs == null) {return as;}if(as == null) {return bs;}ae.next = null;be.next =as;return bs;

        题解思路

        1.首先,定义空引用 bs 和 be 作为 小于x 的节点存放处;定义空引用 as 和 ae 作为 大于或等于 x 的节点存放处。

        2.定义 引用 cur ,遍历链表,若 空引用 bs 和 be 或者 as 和 ae 第一次 接收节点,那么把他们都赋值 为 第一次传过来的节点引用,后续就只让 be 或者 ae 往后走。

        3.遍历完成后,分三种情况:

        一是没有小于x 的部分 节点,bs 和 be 还是空引用,那么直接返回 as  ,也就是原链表。

        二是没有大于x 的部分 节点,as 和 ae 还是空引用,那么直接返回 bs, 也就是原链表。

        三是双方都有节点,而要把 ae 当前的节点的next置为null ;因为cur 遍历完链表的最后一个节点不知道是放在 be 还是 ae ,所以要手动把 ae 的next 置为null ,再将其两部分链接起来返回 bs 

        

        7.链表的回文结构。题目链接

        题解代码:

if(A == null) {return false;} if(A.next == null) {return true;}ListNode fast = A;ListNode slow = A;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}ListNode cur = slow.next;while(cur != null) {ListNode curN = cur.next;cur.next = slow;slow = cur;cur = curN;}while(A != slow) {if(A.val != slow.val) {return false;}if( A.next == slow) {return true;}A = A.next;slow = slow.next;}return true;

        题解思路

        1.首先,判断链表是否为空 或者 链表只有一个节点的情况。

        2.大体思路是将后一半的链表反转过来,再通过起始节点往后遍历 与 结尾节点往前遍历 对比val。

        3.有个特殊情况,就是链表节点个数如果为偶数的话,就需要 A.next == slow 的判断情况,,因为前面已经判断过这两个节点的val了,是相等才能走到 A.next == slow 的判断,若是这种情况,说明到中间了,直接返回 true 

        

        8.输⼊两个链表,找出它们的第一个公共结点。题目链接

          题解代码

ListNode curL = headA;ListNode curS = headB;int pl = 0;int ps = 0;while(curL != null) {pl++;curL =curL.next;}while(curS != null) {ps++;curS =curS.next;}int len = pl - ps;curL = headA;curS = headB;if(len < 0) {len = ps - pl;curL = headB;curS = headA;}while(len != 0) {curL = curL.next;len--;}while(curL != curS) {curL = curL.next;curS = curS.next;}if(curL == null) {return null;}return curS;

        题解思路

        1.首先,我们假设 长节点是headA,短节点 是headB,再求出他们各自的链表长度,再减去得到长度差。

        2.得到的长度差如果是小于零的,那么 长链表是 headB,短链表是headA

        3.先让长链表走 len 布,再让两个链表同时一起走,那么循环到相同节点就是他们的链表的相交节点,还要通过 curL == null 判断有没有相交,如果没相交返回null 。

        

        9.给定⼀个链表,判断链表中是否有环。题目链接

        题解代码

 ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if(fast == slow) {return true;}}return false;

        题解思路

       1. 先定义一个快指针,一个慢指针。

        2.每次循环,快指针走两步,慢指针走一步。

        3.每次循环判断一次 两个引用是否相等,如果相等说明链表存在环,若循环结束了 ,说明fast 走到链表尾部 ,该链表没有形成环。

        

        10.给定⼀个链表,返回链表开始入环的第⼀个节点。如果链表无环,则返回 NULL 。题目链接

        题解代码

 ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if(fast == slow) {while(head != slow) {head = head.next;slow = slow.next;}return slow;}}return null;

        题解思路

         1. 先定义一个快指针,一个慢指针。

        2.每次循环,快指针走两步,慢指针走一步。

        3.每次循环判断一次 两个引用是否相等,如果相等说明链表存在环,若循环结束了 ,说明fast 走到链表尾部 ,该链表没有形成环,返回null 。

        4.存在环的情况下,看下图:

        

         c 代表是圆环的周长,所以 head 离 入环点 等于 y ,所以通过上述 head 和 slow 一起走 的代码即可找到入环点。

        

        

        四. 模拟实现 LinkedList 的一些方法。

         注意:

                在Java的中LinkedList底层实现的是双向链表。

         代码实现

//任意位置插入public void addIndex(int index,int data) {int len = size();if(index < 0 || index > size()) {throw new IndexOutOfBoundsException("双向链表index位置不合法: "+index);}if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}ListNode node = new ListNode(data);ListNode cur = Findindex(index);node.next = cur;cur.prev.next = node;node.prev = cur.prev;cur.prev = node;}//寻找插入节点的后一个节点public ListNode findindex(int index) {ListNode cur = head;while(index != 0) {cur = cur.next;index--;}return cur;}//头插法public void addFirst(int data) {ListNode node = new ListNode(data);if(head == null) {head = node;last = node;}else {node.next = head;head.prev = node;head = node;}}//尾插法public void addLast(int data) {ListNode node = new ListNode(data);if(head == null) {head = node;last = node;}else {last.next = node;node.prev = last;last = node;}}

        注意:

        prev 是指向前一个节点,next 是指向下一个节点。 

        代码分析

        这里我们实现了头插法,尾插法,任意下标插入的方法。

        1.头插法

        首先定义一个要插入的新节点 node ,再判断当前是不是空链表,如果是空链表,说明此时要插入的是第一个元素;如果不是空链表,再通过先绑定后面,再绑定前面的操作进行插入元素。

        2.尾插法

         首先定义一个要插入的新节点 node ,再判断当前是不是空链表,如果是空链表,说明此时要插入的是第一个元素;如果不是空链表,再通过先绑定后面,再绑定前面的操作进行插入元素。

        3.任意下标插入法

         首先,要判断准备要插入的下标是否合法,通过 size()方法,得到链表的节点个数,如果下标不合法就抛出个异常。

        其次,如果下标是 0 或者 是 size(),证明是头插法或者是尾插法,调用其对应的方法就行了。

        最后,findindex 方法找到 准备插入新节点的后一个节点,再通过 “反C” 方法 绑定。

        具体什么是 “反C” 方法,看图:

        

        按照重新链接起来的节点,序号1,2,,3,4代表先后顺序, 因为酷似 “C”,顺序像从C的底部开始往上走,所以被我称作 “反C”方法。

        

        五. LindedList 的构造:

         LinkedList 有两种构造方法:

        

         上面的是无参的构造方法;

        下面的构造方法是这样的:

        先看图:

           

         

        这样的构造方法是可行的,为什么?

        因为 list 实现了 Collection 接口,并且当前的 list 是 list2 的同类型,(list 是 list2 的子类也行 ),所以是没问题的。

        

        但是这样就不行了:

        

        虽然 list 实现了 Collection 接口,但是 String 并不是 Integer 的子类。

        

        所以必须要满足两个条件才可以实现带参数构造LinkedList。

        

        六. LinkedList 常用方法:

        

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

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

相关文章

手机实时提取SIM卡打电话的信令声音--社会价值(一、方案解决了什么问题)

手机实时提取SIM卡打电话的信令声音 --社会价值(一、方案解决了什么问题) 一、前言 这段时间&#xff0c;我们在技术范围之外陷入了一个自证或者说下定义的怪圈&#xff0c;即要怎么样去介绍或者描述&#xff1a;我们是一个什么样的产品。它在当前这个世界上&#xff0c;处于…

康谋方案 | 多源相机数据采集与算法集成测试方案

目录 一、相机组成 二、多源相机采集与测试方案 三、应用案例分享 四、结语 在智能化技术快速发展当下&#xff0c;图像数据的采集与处理逐渐成为自动驾驶、工业等领域的一项关键技术。高质量的图像数据采集与算法集成测试都是确保系统性能和可靠性的关键。随着技术的不断进…

C# 探险之旅:第十一节 - 循环(foreach):一场“遍历”奇幻岛的大冒险!

嘿&#xff0c;勇敢的探险家们&#xff01;欢迎来到C#奇幻岛的第十一站——“遍历”奇幻岛&#xff01;今天&#xff0c;我们要乘坐一艘叫做foreach的魔法船&#xff0c;去遍历&#xff08;也就是一个一个看过来&#xff09;岛上那些神秘的宝藏箱&#xff01;准备好了吗&#x…

LearnOpenGL学习(高级OpenGL -> 高级GLSL,几何着色器,实例化)

高级GLSL 内建变量 顶点着色器 gl_PointSoze : float 输出变量&#xff0c;用于控制渲染 GL_POINTS 型图元时&#xff0c;点的大小。可用于粒子系统。将其设置为 gl_Position.z 时&#xff0c;可以使点的距离越远&#xff0c;大小越大。创建出类似近视眼看远处灯光的效果 gl…

java之集合(详细-Map,Set,List)

1集合体系概述 1.1集合的概念 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组&#xff0c;但集合的大小可变&#xff0c;开发中也非常常用。 1.2集合分类 集合分为单列集合和多列集合 Collection代表单列集合&#xff0c;每个元素&#xff08;数据&#xff…

手机租赁系统开发指南一站式服务流程解析

内容概要 手机租赁系统的开发是一个复杂但有趣的过程&#xff0c;像搭建乐高一样&#xff0c;只要找到合适的模块&#xff0c;就能打造出一个宾至如归的租赁平台。在这部分&#xff0c;我们将对开发流程的整体结构进行简要概述&#xff0c;并指出每个环节的重要性。 首先&…

CS5563国产DP转HDMI芯片支持10k@60Hz视频转换IC

CS5563 4Lane DP to HDMI2.1 10k60Hz适用各种TYPEC/DP转HDMI8K60HZ方案 。深圳市安格瑞科技代理的ASL芯片系列DP转HDMI视频转换方案提供了CS5263、CS5563三个型号供选择。这些芯片在性能和功能上各有特点&#xff1a; CS5263专为高性能打造&#xff0c;支持DP到HDMI2.0的4k60H…

JS进阶DAY5|本地存储

嗨&#xff5e;&#x1f44b; 欢迎来到JavaScript本地存储的世界。在这里&#xff0c;我们可以像烘焙师一样&#xff0c;将数据烘焙成小饼干&#xff08;cookies&#xff09;&#xff0c;或者将它们保存在冰箱&#xff08;localStorage&#xff09;里&#xff0c;以便下次访问时…

HTA8998 实时音频跟踪的高效内置升压2x10W免电感立体声ABID类音频功放

1、特征 输出功率(fIN1kHz,RL4Ω&#xff0c;BTL) VBAT 4V, 2x10.6W(VOUT9V,THDN10%) VBAT 4V, 2x8.6W (VOUT9V,THDN1%) 内置升压电路模式可选择:自适应实时音频跟踪 升压(可提升播放时间50%以上)、强制升压 最大升压值可选择&#xff0c;升压限流值可设置 ACF防破音功能 D类…

深度学习:重塑学校教育的未来

摘要&#xff1a;本文旨在全面剖析深度学习技术在教育领域的应用现状及未来前景。通过对当前深度学习技术在教育中的应用案例进行深入剖析&#xff0c;探讨其在教学效果、学习体验等方面的积极作用&#xff0c;同时分析存在的挑战与问题。在此基础上&#xff0c;本文将进一步展…

React - echarts 世界地图,中国地图绘制

中国地图 首先需要一个包含中国所有省份名称的 json&#xff0c;这个好多网站都能找到。 我传到资源里了&#xff0c;放百度网盘怕太长时间不登录给我删掉了。 中国地图中文版json 我把地图抽出来单独做成了组件&#xff0c;这样用的时候比较方便. 使用的时候&#xff1a; …

【Redis源码】网络模型

Redis源码解析网络模型 基于Redis7源码的网络模型解析 前置准备 源码地址&#xff1a;https://github.com/redis/redis Ide&#xff1a;Clion 网络模型 流程节点下方是源码中对应的方法 总结点 Redis 的网络是IO多路复用指令还是单线程串行 扩展的线程池&#xff0c;协助主…

Python基础笔记17--面向对象(其他)

一、面向对象的三大特性 1、封装 1、 将属性和⽅法书写到类的⾥⾯的操作 2、封装可以为属性和⽅法添加私有权限 2、继承 1、⼦类默认继承⽗类的所有属性和⽅法 2、⼦类可以重写⽗类属性和⽅法 3、多态 1、 传⼊不同的对象&#xff0c;产⽣不同的结果 二、多态 多态指⼀类事…

【QNX】RUN模型的时候,如何监测HTP的使用率?

首先,RUN模型的时候,如何监测HTP的使用率? 使用HTP运行设备端模型的步骤可参考:【QNX】车芯 | 设备端使用HTP运行模型-CSDN博客 监测HTP的使用率的方案如下:设备上,需要起两个窗口。第一个窗口用于跑模型,第二个窗口用于监测。 具体步骤如下:

Spann3R:基于DUSt3R的密集捕获数据增量式重建方法

来自作者Hengyi Wang在b站3D视觉工坊中对于该论文透彻的讲解&#xff0c;这里是相关重要部分的截屏。这篇博客的用途主要是自己做记录&#xff0c;其次分享给感兴趣的同学&#xff0c;最后谢谢作者大佬的认真讲解。 作者是按照这样的次序来介绍的&#xff1a; 首先从传统的三…

Qt 联合Halcon配置

文章目录 配置代码窗口绑定 配置 选择添加库 选择外部库 LIBS -LC:/Program Files/MVTec/HALCON-17.12-Progress/lib/x64-win64/ LIBS -lhalconcpp\-lhdevenginecpp\-lhalconINCLUDEPATH C:/Program Files/MVTec/HALCON-17.12-Progress/include DEPENDPATH C:/Program Fil…

Ansible自动化运维(三)playbook剧本详解

Ansible自动化运维这部分我将会分为五个部分来为大家讲解 &#xff08;一&#xff09;介绍、无密钥登录、安装部署、设置主机清单 &#xff08;二&#xff09;Ansible 中的 ad-hoc 模式 模块详解&#xff08;15&#xff09;个 &#xff08;三&#xff09;Playbook 模式详解 …

React Router 6的学习

安装react-router-dom npm i react-router-dom 支持不同的路由创建 createBrowserRouter 特点 推荐使用的方式&#xff0c;基于 HTML5 的 History API。支持用户友好的 URL&#xff0c;无需 #。适用于生产环境的绝大多数场景。 适用 使用现代浏览器&#xff0c;支持 pus…

jQuery漏洞——CVE-2020-11022/CVE-2020-11023,保姆篇---春不晚

漏洞号&#xff1a;CVE-2020-11022/CVE-2020-11023 漏洞概况及影响 该类风险为应用安全缺陷类DXSS攻击&#xff0c;攻击者可以利用该漏洞注入恶意脚本代码&#xff0c;并在受害者的浏览器上执行。将导致受害者的个人信息泄露、账户被劫持、会话被劫持等安全问题。 一、漏洞版…

Qt编写的文件传输工具

使用QT编写的文件传输工具 文件传输工具通过发送udp广播消息将IP广播给其他开启该程序的局域网机器 文件传输工具 通过发送udp广播消息将IP广播给其他开启该程序的局域网机器 收到的广播消息可以显示在IP地址列表中&#xff0c;点击IP地址可以自动填充到IP地址栏内 选择文件…