链表带环的问题研究及代码实现

链表带环的问题研究及代码实现


[TOC]
完整源代码见:
https://github.com/lankuohsing/DataStructureInCPP/tree/master/DataStructureInCPP/Linked_List_with_Loop
注:如无特别说明,本文中的链表均含有附加表头结点first

1. 如何判断链表是否有环?

思路是让两个指针slow和fast同时从链表头出发遍历链表,fast的速度是slow的两倍(为简单起见我们可以让slow每次走一个节点,fast每次走两个节点),若slow和fast相遇,则说明有环。

代码如下:

bool::Linked_List_with_Loop::isLoop ( )
{
    LinkNode*slow, *fast;

    slow = fast = first->link;
    if ( fast==NULL )
    {
        return false;
    }
    while ( fast->link&&fast->link->link )
    {
        slow = slow->link;
        fast = fast->link->link;
        if ( slow==fast )
        {
            return true;
        }
    }
}

2. 如何计算环的长度?

先给答案:第一次相遇(超一圈)时开始计数,第二次相遇时停止计数
具体推导如下:
带环的链表示意图
如上图所示,设链表总长为L,链表头距离环的入口长度为a,环的入口点距离相遇点长度为x,环的长度为R
设slow的速度为v,fast的速度为2v。无论它们在环上哪一点相遇,它们再次相遇所需时间为R/(2v-v)=R/v。到下一次相遇时,slow走的距离刚好为R,也即得到环的长度。
代码如下:

int Linked_List_with_Loop::getLoopLength ( )
{
    LinkNode*slow, *fast;
    slow = fast = first->link;
    int loopLength = 0;
    if ( fast == NULL )
    {
        return loopLength;
    }
    bool isMeet = false;
    while ( fast->link&&fast->link->link )
    {

        slow = slow->link;
        fast = fast->link->link;
        if ( slow == fast )
        {
            isMeet = true;
        }
        if ( isMeet )
        {
            loopLength++;
            if ( slow->link == fast->link->link )
            {
                break;
            }
        }
    }
    return loopLength;
}

3. 如何得到环的入口点

先给答案:让两个指针分别从表头和相遇点开始走,速度一样,均为每次走一个节点,相遇时的那个点就是连接点。
带环的链表示意图
首先我们证明,当slow和fast相遇时,slow还没走完环的一圈,而fast已经走了超过一圈的路程。证明如下:
假设当slow刚到入口点时,fast已经走了nR+b( n0,,0b<R ,n和b不同时为0)。当b=0时,说明slow和fast在入口点相遇,这时slow显然还没走完环的一圈,fast走完了大于1的整数圈;当 0<b<R 时,fast追上slow的时间为(R-b)/v,追及过程中slow走的距离为R-b,也即说明slow还没走完一圈,而fast走过的距离为2R-2b,加上之前走的nR+b,总共走完了环上的距离为(2+n)R-b>R。得证。
进一步我们可以得到相遇点距离入口点的长度为b
第一次相遇时,slow走了s=a+x步,fast走了2s=s+nR步,也即a+x=nR,也即a=(n-1)R+R-x,也即说明如果此时两个指针分别从表头和相遇点同时同速走,一定会在环的入口点相遇。
代码如下:

LinkNode * Linked_List_with_Loop::getLoopStartNode ( )
{
    LinkNode*slow, *fast;
    LinkNode * loopStartNode = NULL;
    slow = fast = first->link;
    if ( fast == NULL )
    {
        return NULL;
    }
    bool isMeet = false;
    while ( fast->link&&fast->link->link )
    {

        slow = slow->link;
        fast = fast->link->link;
        if ( slow == fast )
        {
            isMeet = true;
            break;
        }

    }
    if ( isMeet )
    {
        fast = first->link;
        while ( true )
        {
            slow = slow->link;
            fast = fast->link;
            if ( slow == fast )
            {
                loopStartNode = slow;
                break;
            }
        }
    }
    return loopStartNode;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值