单链表---成环问题详解

成环问题一---判断链表是否成环

链表成环,即尾结点的next指针不再指向空NULL,而是指向了该链表中的某一个结点而从该结点处形成了一个环。

那么对于成环链表而言,我们使用快慢指针fast与slow,两者每次差一步,那么若链表成环,fast指针会走到slow的后面,然后反过来追到slow,那么当fast能够追到slow,那就说明该链表成环,循环条件依旧是fast和fast->next不为NULL,如果不成环就能够跳出循环返回false。

那么判断是否成环就很简单了,代码如下:

bool hasCycle(struct ListNode *head) {
    struct ListNode* slow ,*fast;
    fast = slow = head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast == slow)
            return true;
    }
    return false;
}

成环问题二---成环请返回入环结点

思路一:相遇点与头结点遍历得到入环节点

通过问题一,我们知道成环的判断方法,那么现在主要的问题就是入环点的求解,我们其实能够知道fast与slow的相遇结点。                                                                                                              那么我们假设头结点到入环点的距离为L环的长度为Cfast与slow相遇点与入环点距离为X。    我们来看成环的情况,画出图:

那么通过上面的图,fast走了---L+n*C+X ;slow走了L+X。

我们又知道fast走的距离是slow的两倍,因此就能够创造一个等式---L+n*C+X = 2*(L+X)

化简---L = n*C - X

这个式子很有意义,放在图里有什么意义呢?L是起点到入环点的距离,n*C-X等价于(n-1)*C+C-X是相遇点在环里跑了n-1圈然后跑到环入口点的路程。L与n*C-X相等,说明同速的相遇点指针与头结点指针一起走,那么就会同时到环入口点,那么就能够判断了。

那么就很简单了,找到相遇点rhead,然后从相遇点rhead开始和头结点head开始,同时遍历直道两者相等返回其中任意一个指针即可。

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow ,*fast;
    fast = slow = head;
    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow=slow->next;
        if(slow == fast)
            {
                struct ListNode* rhead = slow;
                while(rhead != head)
                {
                    rhead =rhead->next;
                    head = head->next;
                }
                return rhead;
            }
    }
    return NULL;
}

还有一个问题:环的入环点一定是尾结点吗?

不一定!当尾结点的next指针指向自己时,入环点才是尾结点,否则,指向哪个结点,入环点就是哪个结点。

思路二:断开相遇点,转换为链表相交问题

如果我们没有想到上述方法,我们也可以换一种思路:转换为链表相交问题

代码如下:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow ,*fast;
    fast = slow = head;
    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow=slow->next;
        if(slow == fast)
            {
                //转换为链表相交问题
                struct ListNode* rhead = slow->next;
                slow->next =NULL;
                struct ListNode* cur = head;
                struct ListNode* rcur = rhead;
                int A = 0;
                int B = 0;
                while(cur)
                {
                    cur=cur->next;
                    A++;
                }
                while(rcur)
                {
                    rcur=rcur->next;
                    B++;
                }
                int n =abs(A-B);
                struct ListNode* Longlist = head;
                struct ListNode* Shortlist = rhead;
                if(A<B)
                {
                    Longlist = rhead;
                    Shortlist = head;
                }
                while(n--)
                {
                    Longlist=Longlist->next;
                }
                while(Longlist!=Shortlist)
                {
                    Longlist = Longlist->next;
                    Shortlist = Shortlist->next;
                }
                return Longlist;
            }
    }
    return NULL;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值