数据结构——链表的带环问题

一.判断链表是否有环

1.用快慢指针来解决,定两个slow,fast两个指针分别指向head

 

2.slow一次走一步,fast一次走两步,slow进环之后开始追击问题,如果相遇就带环。

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

这里有几个小问题:

(1).为什么会相遇,有没有可能错过,永远都追不上,请证明

假设slow和fast两个指针在如图所在的位置,并设其之间的距离为N,进行追击距离会从N,N-1,N-2......3,2,1,0,逐渐减小为0,最后相遇。

(2).slow走一步,fast走n步(n>=3)可能相遇吗,请证明

假设fast一次走3步,slow,fast指针如图所示,设其之间的距离为N。

slow走一步,fast走三步

所以其中的距离,假如N为偶数,那么距离变化为N,N-2,N-4......4,2,0;如果N为奇数,那么距离变化为N,N-2,N-4......3,1,-1。

如果错过,距离为-1,则开始新一轮的追击,设环形周长为C,所以距离变成了C-1。

这里也分两种情况。

若C-1为偶数即沿用N是偶数的情况,最后距离会变成0,相遇。

若C-1为奇数,那就永远都追不上。

通过以上的案例你可以发现,追击问题中,速度差是关键!

例如,slow走1步,fast走4步,速度差是3

假设进环的距离为N。N%3==0,N%3==1,N%3==2,分别是三种状况。

又从以上案例你会发现,当N是奇数,C是偶数,则永远都追不上。

假设slow进环时,fast跟slow距离是N

slow走的距离是:L

fast走的距离是:L+x*C

slow进环时,假设fast已经在环里转了x圈

所以当fast走的距离是slow的3倍时,有关系式:

3*L=L+x*C+C-N

化简得:2*L=(x+1)*C-N,

假如N为奇数,即偶数=(x+1)*偶数-奇数,等式不成立,只有奇数-奇数才能等于偶数。反证出N是奇数,C是偶数,则永远都追不上。

结论:一定能追上,N是偶数第一轮就追上了;N是奇数第一轮追不上,C-1是偶数第二轮就追上了。

二.寻找环的入口点

struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode* fast=haed, *slow=head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇
        if(slow == fast)
        {
            struct ListNode* meet = slow;
            while(meet != head)
            {
                meet=meet->next;
                head =head->next;
            }
            return meet;
        }
    }
    return NULL;
}

相遇时:

slow走过的路程:L+N

fast走过的路程:L+x*C+N

fast走的路程是slow的2倍

2*(L+N)=L+x*C+N

化简可得:L=x*C-N

即L=(x-1)*C+C-N

图中绿色的部分相等。

还有另一种解法,将问题转变成链表的相交。

struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB)
{
    if(headA==NULL && headB==NULL)
    {
        return NULL;
    }
    struct ListNode* curA = headA, * curB = headB;
    int count1 = 0, count2 = 0;
    while (curA->next != NULL)
    {
        count1++;
        curA = curA->next;
    }
    while (curB->next != NULL)
    {
        count2++;
        curB = curB->next;
    }
    int gap = abs(count1 - count2);
    struct ListNode* longlist = headA;
    struct ListNode* shortlist = headB;
    if (count1 < count2)
    {
        longlist = headB;
        shortlist = headA;
    }
    if (curB == curA)
    {
        while (gap--)
        {
            longlist = longlist->next;
        }
        while (longlist != shortlist)
        {
            longlist = longlist->next;
            shortlist = shortlist->next;
        }
        return longlist;
            
    }
    else
    {
        return NULL;
    }
}
 
struct ListNode* getmeetnode(struct ListNode* head)
{
    struct ListNode* fast, * slow;
    fast = slow = head;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
        {
            return fast;
        }
    }
    return NULL;
}
struct ListNode* detectCycle(struct ListNode* head)
{
    // 找到相遇点
    
    struct ListNode* meetnode = getmeetnode(head);
    if(meetnode == NULL)
    {
        return NULL;
    }
    // 断开为两个链表
    struct ListNode* cur1, *cur2;
    cur1 = meetnode->next;
    meetnode->next=NULL;
    cur2 = head;
 
    // 找到两个链表的交点
    struct ListNode* IntersectionNode = getIntersectionNode(cur1, cur2);
    return IntersectionNode;
 
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值