23 链表中环的入口结点

题目

如果一个链表中包含环,如何找到环的入口节点?
例如:1–>2–>3–>4–>5–>6(–>3),在6后并没有结束,其next指向了中间的3,形成了一个环,所以环的入口节点是节点3。

牛客网 OJ
AcWing OJ

C++ 题解

  • 第一步确定链表中是否包含环,我们定义两个指针:快慢指针,慢指针每次走一步,快指针每次走两步,如果它俩重合了,这就说明链表中存在环。
  • 第二步求环的长度,两者碰头后,让其中一个继续走,每走一步步数加一,然后求得环的长度。
  • 第三步求环的初始节点,仍然是两个指针,其中一个事先走环长个节点,假设环长为n,然后另一个指针从头开始移动,两者之间的间距为n一直保持,那么当两者碰头时,相遇额节点就是环的入口节点,此时第一个指针比第二个指针正好多走了环长(n)个节点。
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode* meet = MeetingNode(pHead);
        if(!meet)
            return nullptr;
        
        ListNode*  p1 = meet->next;
        unsigned int NodeNum = 1;
        while(p1 != meet)
        {
            p1 = p1->next;
            NodeNum++;
        }
        
        p1 = pHead;
        ListNode*  p2 = pHead;
        for (int i=0;i<NodeNum;i++)
            p1 = p1->next;
        
        while(p1!=p2)
        {
            p1 = p1->next;
            p2 = p2->next;
        }
        return p1;
    }
    
    //  判断链表中是否有环
    ListNode* MeetingNode(ListNode* pHead)
    {
        if (pHead == nullptr)
            return nullptr;

        ListNode* pSlow = pHead->next;
        if (pSlow == nullptr)
            return nullptr;

        ListNode* pFast = pSlow->next;

        // 慢指针每次前进一步
        // 快指针每次前进两步
        // 在前进过程中不可能碰到nullptr,一旦碰到,跳出循环,并且链表中无环
        while (pFast != nullptr && pSlow != nullptr)
        {
            // 两个指针相遇,说明链表有环,返回相遇节点
            if (pFast == pSlow)
                return pFast;

            pSlow = pSlow->next;
            pFast = pFast->next;

            if (pFast != nullptr)
                pFast = pFast->next;
        }

        return nullptr;
    }
    
};

python 题解

方法一

思路与C++题解一致

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        pMeet = self.Meeting(pHead)
        if not pMeet:
            return None
        
        p1 = pMeet.next
        nodeNum = 1;
        while p1 != pMeet:
            p1 = p1.next
            nodeNum += 1
            
        p1 = pHead
        p2 = pHead
        for i in range(nodeNum):
            p1 = p1.next
        
        while p1 != p2:
            p1 = p1.next
            p2 = p2.next
            
        return p1
    
    def Meeting(self,pHead):
        if not pHead:
            return None
        pSlow = pHead
        pFast = None
        if pSlow.next:
            pFast = pSlow.next
        
        while pSlow and pFast:
            if pFast == pSlow:
                return pFast
            
            pSlow = pSlow.next
            pFast = pFast.next
            if pFast.next:
                pFast = pFast.next
        
        return None

方法二

  • 两个指针一个fast、一个slow同时从一个链表的头部出发,fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇。
  • 此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内),这次两个指针一次走一步,相遇的地方就是入口节点。

上述关系的证明:

  • 链表头到环入口长度为--a
  • 环入口到相遇点长度为--b
  • 相遇点到环入口长度为--c

则:相遇时:
快指针路程=a+(b+c)k+b ,k>=1 其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。
慢指针路程=a+b
快指针走的路程是慢指针的两倍,所以:(a+b)x 2 = a+(b+c)k+b
化简可得:**a=(k-1)(b+c)+c **
这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if not pHead:
            return None
        
        pFast = pHead
        pSlow = pHead
        
        while pFast and pFast.next:
            pFast = pFast.next.next
            pSlow = pSlow.next
            if pFast == pSlow:
                break
        
        if pFast == None or pFast.next == None:
            return None
            
        pFast = pHead
        while pFast != pSlow:
            pFast = pFast.next
            pSlow = pSlow.next
        
        return pSlow

注意:

  • 此时不需要统计环中节点的数目。
posted @ 2019-03-05 08:53  youngliu91  阅读(106)  评论(0编辑  收藏  举报