当前位置: 首页 > news >正文

【LC】反转链表, 相交链表, 环形链表

在这里插入图片描述博客主页: XIN-XIANG荣
系列专栏:【LeetCode刷题】
一句短话: 难在坚持,贵在坚持,成在坚持!

文章目录

  • 1. 反转链表(lc206)
    • 题目描述:
    • 解题思路:
    • 代码实现:
    • 提交结果:
  • 2. 相交链表(lc160)
    • 题目描述:
    • 解题思路:
    • 代码实现:
    • 提交结果:
  • 3. 环形链表(lc141)
    • 题目描述:
    • 解题思路:
    • 代码实现:
    • 提交结果:
  • 4. 环形链表||(lc142)
    • 题目描述:
    • 解题思路:
    • 代码实现:
    • 提交结果:

1. 反转链表(lc206)

题目描述:

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

img

示例 1

输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

示例 2

输入:head = [1,2]

输出:[2,1]

示例 3

输入:head = []

输出:[]

提示

链表中节点的数目范围是 [0, 5000]

-5000 <= Node.val <= 5000

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/reverse-linked-list

解题思路:

遍历链表 , 将链表的第一个节点当作新链表的头节点 , 将其他节点完成头插操作即可 , 要注意的是节点的头插操作要改变节点中指针域引用的指向 , 所以在完成头插操作前 , 需要先记录下一个节点的位置 , 否则会与下一个节点失联 .

代码实现:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //判断是否为空链表
        if(head == null) {
            return null;
        }
        //链表中是否只有一个节点
        if(head.next == null) {
            return head;
        }
        ListNode cur = head.next;
        head.next = null;
        //从第二个节点开始进行头插
        while(cur != null) {
            //记录下一个节点
            ListNode curNext = cur.next;
            cur.next = head;
            head = cur;
            cur = curNext;
        }
        return head;
    }
}

提交结果:

img

2. 相交链表(lc160)

题目描述:

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

示例 1

img

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3

输出:Intersected at ‘8’

解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。

从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。

在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2

img

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1

输出:Intersected at ‘2’

解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。

从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。

在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3

img

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2

输出:null

解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。

由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。

这两个链表不相交,因此返回 null 。

提示

listA 中节点数目为 m

listB 中节点数目为 n

1 <= m, n <= 3 * 104

1 <= Node.val <= 105

0 <= skipA <= m

0 <= skipB <= n

如果 listA 和 listB 没有交点,intersectVal 为 0

如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/intersection-of-two-linked-lists

解题思路:

两个链表的相交节点的引用一定是相同的 , 题目需要我们找到这个节点并返回 ;

可以这样实现 , 先分别求出两个链表的长度并求出两个链表长度的差值len , 声明引用 pl(指向长链表) 和 ps(指向短链表) , 让pl先走len步 , 此时再让pl和ps同时走 , 如果 pl 和 ps 相等且不为null , 那么此时就找到了相交的节点 .

代码实现:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
       //求出两个链表的长度并进行比较
        int lenA = 0;
        int lenB = 0;
        ListNode pl = headA;//pl指向长链表
        ListNode ps = headB;//ps指向短链表
        while(pl != null) {
            pl = pl.next;
            lenA++;
        }
        while(ps != null) {
            ps = ps.next;
            lenB++;
        }
        pl = headA;
        ps = headB;
        
        int len = lenA - lenB;
        if(len < 0) {
            pl = headB;
            ps = headA;
            len = lenB- lenA;
        }
        //让较长链表先走len步
        while(len > 0) {
            pl = pl.next;
            len--;
        }
        //此时pl和ps同时走
        while(pl != ps) {
            ps = ps.next;
            pl = pl.next;
        }
        if(pl != null) {
            return pl;
        }
        return null;
    }
}

提交结果:

img

3. 环形链表(lc141)

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1

img

输入:head = [3,2,0,-4], pos = 1

输出:true

解释:链表中有一个环,其尾部连接到第二个节点。

示例 2

img

输入:head = [1,2], pos = 0

输出:true

解释:链表中有一个环,其尾部连接到第一个节点。

示例 3

img

输入:head = [1], pos = -1

输出:false

解释:链表中没有环。

提示

链表中节点的数目范围是 [0, 10 ^ 4]

-105 <= Node.val <= 105

pos 为 -1 或者链表中的一个 有效索引 。

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/linked-list-cycle

解题思路:

快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针同时从链表起始位置移动,快指针一次移动两个节点 , 慢指针一次移动一个节点 , 如果链表带环则一定会在环中相遇 ; 否则快指针率先走到链表的末尾 , 此时表明链表不带环。

这里在解释一下为什么两个指针一定会在环中相遇 , 如果环存在的话 , 那么快指针一定先比慢指针先进入环 , 当慢指针进入环后 , 此时两个指针不管相差多少个节点 , 每次移动快指针走两步 , 慢指针走一步, 这样每次移动两个震指针之间的距离都会缩小一步 , 最终一定会相遇 (想象生活中在操场跑步) .

【扩展思考】:

  • 为什么快指针每次走两步,慢指针走一步可以实现相遇, 快指针设置为一次走3步,走4步,…n步可以吗?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快 指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离 就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指 针的,即相遇。

img

所以我们这里最好将快指针的步长设置为2步, 防止套圈情况的出现 .

代码实现:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        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;
     }
}

提交结果:

img

4. 环形链表||(lc142)

题目描述:

给你一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1

img

输入:head = [3,2,0,-4], pos = 1

输出:返回索引为 1 的链表节点

解释:链表中有一个环,其尾部连接到第二个节点。

示例 2

img

输入:head = [1,2], pos = 0

输出:返回索引为 0 的链表节点

解释:链表中有一个环,其尾部连接到第一个节点。

示例 3

img

输入:head = [1], pos = -1

输出:返回 null

解释:链表中没有环。

提示

链表中节点的数目范围是 [0, 10 ^ 4]

-105 <= Node.val <= 105

pos 为 -1 或者链表中的一个 有效索引 。

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/linked-list-cycle

解题思路:

img

说明:

H为链表的起始点,E为环入口点,M为判断是不是环的时候的相遇点

设:

环的长度为R,H到E的距离为L ; E到M的距离为X

则M到E的距离为R-X

在判断是不是环的时候,快慢指针相遇时所走的路径长度:

fast: L +X +nR

slow: L + x

注意:

1.当慢指针进入环时,快指针可能已经在环中绕了n圈了,n至少为1 , 因为快指针先进环走到M的位置(此时慢指针还在还没有到过M),两个指针最后又在M的位置相遇 ;

2.慢指针进环之后,快指针肯定会在慢指针走一圈之内追上慢指针 , 因为慢指针进环后,快慢指针之间的距离最多就是环的长度,而两个指针在移动时,每次它们至今的距离都缩减一步,因此在慢指针移动一圈之前快指针肯定是可以追上慢指针的 .

快指针速度是满指针的两倍,因此有如下关系是:

2*(L+ X) =L+ X + nR

L+ X = nR

L = nR - X

所以我们让一个指针从链表起始位置运行,一个指针从相遇点位置绕环,每次都走一步,两个指针最终会在入口点的位置相遇 (这里可能不好理解 , 可以看下面的图理解一下)

img

代码实现:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                break;
            }
        }
        if(fast == null || fast.next == null) {
            return null;
        }
        //找到入口点
        while(head != fast) {
            head = head.next;
            fast = fast.next;
        }
        return fast;
    }
}

提交结果:

img

相关文章:

  • 【Java】之集合总结(上)
  • Redis中加锁的lua脚本的源码
  • Mac电脑解决Google翻译失效实用方法
  • 【易购管理系统】商品列表
  • 北斗导航 | RTKLib中的模型和算法(一)—— 时间系统
  • 【论文阅读】自动作文评分系统:一份系统的文献综述
  • avformat_open_input() 代码分析
  • Spring Bean的生命周期、Java配置BeanFactoryPostProcessor失效与解决
  • 大模型系统和应用——高效训练模型压缩
  • “华为杯”第十八届中国研究生数学建模竞赛一等奖经验分享
  • C#的StreamReader类使用说明
  • 基于图搜索的规划算法之 A* 家族(九):Hybrid A* 算法
  • 2022年Webpack 5初学者完整指南
  • 【MATLAB教程案例22】基于MATLAB图像去噪算法仿真——中值滤波、高斯滤波以及频域滤波等
  • 浙江大学软件学院2022保研经历分享
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • FastReport在线报表设计器工作原理
  • HashMap剖析之内部结构
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • iOS 系统授权开发
  • Mocha测试初探
  • MYSQL 的 IF 函数
  • Nodejs和JavaWeb协助开发
  • node学习系列之简单文件上传
  • 程序员该如何有效的找工作?
  • 关于Flux,Vuex,Redux的思考
  • 好的网址,关于.net 4.0 ,vs 2010
  • 和 || 运算
  • 聊聊flink的TableFactory
  • 漂亮刷新控件-iOS
  • 温故知新之javascript面向对象
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 运行时添加log4j2的appender
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • #define,static,const,三种常量的区别
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (js)循环条件满足时终止循环
  • (备忘)Java Map 遍历
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (一) springboot详细介绍
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)fock函数详解
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)http协议
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)菜鸟学数据库(三)——存储过程
  • (转)重识new
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .Net 4.0并行库实用性演练
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes