142.环形链表II(中等)

题目链接:环形链表II

题目描述:

给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回 null
为了表示给定链表中的环,我们使用整数 pos来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。
注意,pos仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以使用 O(1) 空间解决此题?

示例 1:

142-1

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

142-2

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

142-3

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:
  • 链表中节点的数目范围在范围 [0, 1 0 4 10^{4} 104] 内
  • - 1 0 5 10^{5} 105 <= Node.val <= 1 0 5 10^{5} 105
  • pos 的值为 -1 或者链表中的一个有效索引

题目分析:

这道题是关于链表找环路的问题,这种题有一个通用的解法,即采用快慢指针法(也称Floyd判圈法)求解。给定两个快慢指针,分别命名为 slow 和 fast,起始位置都指向链表的开头。每次 fast 前进两步,slow 前进一步。如果 fast可以走到尽头,那么说明没有环路;如果 fast 可以无限走下去,那么说明一定有环路,且一定存在一个时刻 slow 和 fast 相遇。这个第一次相遇很好理解。当 slow 和 fast 第一次相遇时,我们将 fast 重新移动到链表开头,并让 slow 和 fast 每次都前进一步。当 slow 和 fast 第二次相遇时,相遇的节点即为环路的开始点。但为什么一定会发生第二次相遇且相遇点就是环路开始的点呢?

我们假设慢指针slow走了n步,快指针fast经过了一圈走了2n步它们刚好第一次相遇。那么第一次相遇的时候快指针与慢指针的行程差是一个环的长度(如图)。
142-j1

设相遇点距环的起点的距离为m步,那么环的起点距头结点head的距离为n-m步,即慢指针从head出发行走n-m步即可到达我们所求的环起点。巧的是一个环的长度刚好为n步,所以从第一次相遇点到环的起点刚好也为n-m步,所以,只要一个指针从head开始走n-m步,同时另一个指针从第一次相遇点沿着环走n-m步刚好能在环的起点相遇。
142-j2

题解一:

执行用时: 0 ms
内存消耗: 38.7 MB

/**
 * 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 slow = head, fast = head;
        // 判断是否存在环路
        do {
            // 如果快指针遇到为空的情况,说明不存在回路
            if (fast == null || fast.next == null)
                // 返回空
                return null;
            // 快指针每次前进2步
            fast = fast.next.next;
            // 慢指针每次前进1步
            slow = slow.next;
            // 当快慢指针第一次相遇,证明有回路,跳出循环
        } while (fast != slow);
        // 快指针指向链表头
        fast = head;
        // 寻找环路节点,当快慢指针第二次相遇,证明此相遇节点为环路的开始点
        while (fast != slow) {
            // 慢指针每次前进1步
            slow = slow.next;
            // 快指针每次前进1步
            fast = fast.next;
        }
        // 返回入环的第一个节点
        return fast;
    }
}

题解二:

执行用时: 1 ms
内存消耗: 37.6 MB

/**
 * 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) {
        // 如果节点只有0个或者1个,根本不可能成环,如果有三个节点,如果第三个节点为null,则没成环
        if(head == null || head.next == null || head.next.next == null) {
            return null;
        }
        // 初始化慢指针为head.next
        ListNode slow = head.next;
        // 初始化快指针为head.next.next
        ListNode fast = head.next.next;
        // 当慢指针没追上快指针
        while(slow != null && fast != null && fast.next != null && slow != fast) {
            // 慢指针移动一步
            slow = slow.next;
            // 快指针移动两步
            fast = fast.next.next;
        }
        fast = head;
        // 将fast返回head,和slow同步进行移动,最后两者相等时则为入环点
        while(slow != null && fast != null && fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

题目来源:力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值