leetcode题解-142. Linked List Cycle II

本文探讨了在链表中查找循环起始节点的方法,包括使用哈希表、暴力匹配及双指针技巧。特别介绍了双指针法的具体实现过程,并通过数学推导解释了其正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

本题与之前的Linked List Cycle差不多,上道题目是判断一个链表是否含有环,这个题目是找到环的开始位置,首先我们不考虑额外的空间要求,那么最简单的方法就是使用Hash表来实现,保存在此之前出现的每个节点,这样只需要判断后来的节点是否已经出现过即可。代码入小所示:

public ListNode detectCycle(ListNode head) {
        HashSet<ListNode> tmp = new HashSet<>();
        while(head != null){
            if(tmp.contains(head))
                return head;
            tmp.add(head);
            head = head.next;
        }
        return null;
    }

这种方法使用了额外的存储空间,而且效率很低,那么我们想想该如何改进呢。先考虑暴力破解法,就是每次遍历一个节点时我们就判断该节点是否在之前出现过,这样使用两次循环来实现,但是由于重复判断次数太多,会导致超时==代码入下所示:

    public ListNode detectCycle1 (ListNode head){
        ListNode cur=head, newHead=head;
        int i = 0;
        while(cur != null){
            for(int j=0; j<i; j++){
                if(cur == newHead)
                    return cur;
                newHead = newHead.next;
            }
            newHead = head;
            cur = cur.next;
            i += 1;
        }
        return null;
    }

所以我们要接着向改进的方法,这里考虑上道题目中使用的fast和slow两个指针的思路,当存在环时,fast最终会追上slow,可是二者交汇的位置并不一定是环的起始位置,如何找到还的起始点呢??有一个很巧妙的想法,那就是当fast==slow时,在新创一个节点从head开始遍历,当他和slow相遇时,就是换的开始位置。大家可以画一个图结合起来思考,这里我们假设链表长度为l,当fast与slow相遇时slow走了x,fast走了2x,那么2x=l+y,y表示环的起始位置到slow的长度,那么从头到环开始位置的长度假设为z,所以就有下面的等式:

2x = l+y
x = z+y
所以l-x = z, 也就是说这样新的节点与slow会在环开始的地方相遇

代码如下所示:

    public ListNode detectCycle2(ListNode head){
        ListNode slow=head, fast=head;

        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if(fast == slow){
                ListNode tmp = head;
                while(tmp != slow){
                    tmp = tmp.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        return null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值