剑指offer面试题23:链表中环的入口节点

题目描述

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

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {

    }
};

思路一:

借助STL

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(pHead==nullptr||pHead->next==nullptr)
            return nullptr;
        map isEntrance;
        ListNode* p=pHead;
        while(p!=nullptr)
        {
            if(++isEntrance[p]==2)
                return p;
            p=p->next;
        }
        return nullptr;
    }
};

思路二(剑指offer):

定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就包含环;如果走得快的指针走到了链表的末尾都没追上第一个指针,那么链表就不包含环。否则,如果链表中的环有n个节点,则指针P1先在链表上向前移动n步,然后两个指针以相同的速度向前移动。当第二个指针指向环的入口节点时,第一个指针已经围绕着环走了一圈,又回到了入口节点。

ListNode* MeetingNode(ListNode* pHead)//在链表中存在环的前提下找打一快一慢两个指针相遇的节点
{
    if(pHead==nullptr)
        return nullptr;

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

    ListNode* pFast=pSlow->next;
    while(pFast!=nullptr&&pSlow!=nullptr)
    {
        if(pFast==pSlow)
            return pFast;
        pSlow=pSlow->next;
        pFast=pFast->next;
        if(pFast!=nullptr)
            pFast=pFast->next;
    }
    return nullptr
}

ListNode* EntryNodeOfLoop(ListNode* pHead)
{
    ListNode* meetingNode=MeetingNode(pHead);
    if(meetingNode==nullptr)
        return nullptr;
    //得到环中节点的数目
    int nodesInLoop=1;
    ListNode* pNode1=meetingNode;
    while(pNode1->next!=meetingNode)
    {
        pNode1=pNode1->next;
        ++nodesInLoop;
    }
    //先移动pNode1,次数为环中节点的数目
    pNode1=pHead;
    for(int i=0;inext;
    //再移动pNode1和pNode2
    ListNode* pNode2=pHead;
    while(pNode1!=pNode2_
    {
        pNode1=pNode1->next;
        pNode2=pNode2->next;
    }
    return pNode1;
}

思路三(参见牛客)思路二更具优越性:


证明:

一个链表中包含环,请找出该链表的环的入口结点。

此问题包含两个步骤:

(1)判断链表中是否有环

(2)找出环

一、

1)选择快慢指针,让快指针每次走两步,慢指针每次走一步,若是单链表中有环的话,那么两个指针会相遇,即指向的相同的节点的值相等来判断。

2)当相遇的时候,慢指针在环中走了k步,设环之外的部分长为x,环的长度为n,则快指针一共走了 x+m*n步,(m为快指针在环中走的圈数),慢指针一共走了x+k步,因为快指针的速度是慢指针的两倍。那么可以得到2(x+k)=x+m*n+k;得到x为m*n-k ,慢指针在圈中还剩下的步数n-k;

二、

让快指针/慢指针从头开始,两个指针每次都走一步,当快指针走了想x(m*n-k)步的时候,到达环的入口,慢指针在圈中走m-1圈加k步的时候,也到达环入口那个节点,两个指针再次相遇,此刻的节点就是环的入口的节点。
原文:https://blog.csdn.net/snow_7/article/details/52181049 
 

  • 第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。
  • 第二步,找环的入口。接上步,当p1==p2时,p2所经过节点数为2x,p1所经过节点数为x,设环中有n个节点,p2比p1多走k圈有2x=n+x; n=x;可以看出p1实际走了一个环的步数,再让p2指向链表头部,p1位置不变,p1,p2每次走一步直到p1==p2; 此时p1指向环的入口。
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(pHead==nullptr||pHead->next==nullptr)
            return nullptr;
        ListNode* p1=pHead;
        ListNode* p2=pHead;
        while(p1!=nullptr&&p2!=nullptr)
        {
            p1=p1->next;
            p2=p2->next;
            if(p2==nullptr)
                return nullptr;
            p2=p2->next;
            if(p1==p2)
            {
                p2=pHead;
                while(p1!=p2)
                {
                    p1=p1->next;
                    p2=p2->next;
                }
                return p1;
            }
        }
        return nullptr;
    }
};

 

你可能感兴趣的:(剑指offer)