【剑指Offer】链表中环的入口结点

题目描述:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出 null 。

解题思路: 可以直接暴力求解,依次将链表中的结点放入一个 List 中,第一个重复的结点就是入口结点,下面给出两种比较巧妙的方法,思想值得学习。

解法一: 双指针。上一题「链表中倒数第 k 个结点」中,利用双指针的思想进行求解,此题同样可以借用这一思路。假设链表长度为 N ,第 N 个结点连接到第 k 个结点形成了环,这样链表中环的入口结点就转化成了倒数第 N-k+1 个结点,N-k+1 即为环中结点的数量,求出环中结点的数量就能计算出入口结点,怎么计算环中结点数量呢?

使用快慢指针,慢指针一次走一步,快指针依次走两步,如果走的过程中快指针追上了慢指针,说明链表中有环(因为在两个指针都进入环的时候,一个步长为 1,一个步长为 2,两个指针之间的距离每走一步就缩小 1 个单位,所以两个指针肯定会再次相遇),并且相遇的位置一定在环内,从相遇结点触发再回到相遇结点的距离就是环的长度。

public ListNode EntryNodeOfLoop(ListNode pHead){
    
    // 寻找相遇结点
    ListNode meetingNode = MeetingNode(pHead);
    
    if(meetingNode == null) return null;
    
    // 计算环中的结点数
    int nodesInLoop = 1;
    ListNode nowNode = meetingNode;
    while(nowNode.next != meetingNode){
        nowNode = nowNode.next;
        nodesInLoop++;
    }
    
    // 快指针前移
    ListNode slow = pHead, fast = pHead;
    while(nodesInLoop > 0){
        fast = fast.next;
        nodesInLoop--;
    }
    
    while(fast != slow){
        slow = slow.next;
        fast = fast.next;
    }
    
    return slow;
    
}

// 寻找相遇节点
public ListNode MeetingNode(ListNode pHead){
    
    if(pHead == null) return null;
    
    ListNode slow = pHead.next;
    if(slow == null) return null;
    
    ListNode fast = slow.next;
    
    while(fast != null && slow != null){
        if(fast == slow) return fast;
        slow = slow.next;
        fast = fast.next;
        if(fast != null) fast = fast.next;
    }
    
    return null;
}

解法二: 数学推导。此方法解释来自牛客网大佬题解


假设 x 为环前面的路程(黑色路程),a 为环入口到相遇点的路程(蓝色路程,假设顺时针走),c 为环的长度(蓝色+橙色路程)。

当快慢指针相遇的时候:

慢指针走过的路程为 S_slow = x + m * c + a
快指针走过的路程为 S_fast = x + n * c + a

2 * S_slow = S_fast
2 * (x + m * c + a) = (x + n * c + a)

从而可以推导出:
x = (n - 2 * m) * c - a = (n - 2 * m - 1) * c + c - a

环前面的路程 = 数个环的长度(可能为 0 ) + c - a

什么是 c - a ? 这是相遇后,环后面部分的路程(橙色路程)。

所以,可以让一个指针从起点 A 开始走,让一个指针从相遇点 B 开始继续往后走,两个指针速度一样,那么,当从原点的指针走到环入口点的时候,从相遇点开始走的那个指针也一定刚好到达环入口点。

public ListNode EntryNodeOfLoop(ListNode pHead){
 
    if(pHead==null || pHead.next==null || pHead.next.next==null)return null;

    ListNode fast = pHead.next.next;
    ListNode slow = pHead.next;

    //先判断有没有环
    while(fast!=slow){
        if(fast.next!=null && fast.next.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }else{
            return null;
        }
    }

    //循环出来的话就是有环,且此时fast==slow.
    fast=pHead;
    while(fast!=slow){
        fast=fast.next;
        slow=slow.next;
    }
    return slow;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值