编程导航算法通关村第1关 | 链表中环的问题

给定一个链表,判断链表中是否有环,这就是LeetCode141。进一步,假如有环,那环的位置在哪里?这就是LeetCode 142题。

判断是否有环

示例1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

在这里插入图片描述

使用hash,遍历时将元素放入map中,如果有环就会发生碰撞

public boolean hasCycle(ListNode head) {
    Set<ListNode> seen = new HashSet<ListNode>();
    while (head != null) {
        if (!seen.add(head)) {
            return true;
        }
        head = head.next;
    }
    return false;
}

使用快慢指针

一个快指针(一次走两步),一个慢指针(一次走一步)。如果快的能到达表尾就不会有环,否则如果存在环,则慢指针一定会在某个位置与快指针相遇。

为什么如果有环一定会相遇

当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。

在这里插入图片描述

public boolean hasCycle(ListNode head) {
    if(head==null || head.next==null){
        return false; 
    }
    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;
}

确定入口方法

使用快慢双指针:设两指针 fastslow 指向链表头部 headfast 每轮走 2 步,slow 每轮走 1 步;
在这里插入图片描述

第一次相遇:

那么我们可以知道fast指针走了a+b+c+b(10)步,
slow指针走了a+b(5)步
那么:
2*(a+b) = a+b+c+b
所以a = c
因此此时让slow从相遇点6继续向前走,fast回到起点,两个同时开始走(两个每次都走一步),一次走一步那么它们最终会相遇在1点,正是环的起始点。

如果是走了多圈之后才遇到

设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为:
Fast: a+n(b+c)+b=a+(n+1)b+nc
根据题意,任意时刻,fast 指针走过的距离都为 slow 指针的 2 倍。因此,我们有:
a+(n+1)b+nc=2(a+b)
也就是:a=c+(n−1)LEN。
由于b+c就是环的长度,假如为LEN,则:
a=c+(n-1)LEN

说明相遇的时候快指针在环了已经转了(n-1)LEN圈,如果n-1就退化成了我们上面说的一圈的场景。假如n是2 ,3,4,…呢,这只是说明当一个指针slow重新开始从head走的时候,另一个指针fast从相遇点开始,两者恰好在入口处相遇,只不过fast要先在环中转n-1圈。

public ListNode detectCycle(ListNode head) {
    if (head == null) {
        return null;
    }
    ListNode slow = head, fast = head;
    while (fast != null) {
        slow = slow.next;
        if (fast.next != null) {
            fast = fast.next.next;
        } else {
            return null;
        }
        if (fast == slow) {
            ListNode ptr = head;
            while (ptr != slow) {
                ptr = ptr.next;
                slow = slow.next;
            }
            return ptr;
        }
    }
    return null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值