剑指offer-15:单链表有趣问题-有环-环长度-环入口等(续)

第15题要求找到倒数第k个结点。采用快慢出发不同的指针,卡出k-1的距离,当快指针到尾结点时,慢指针刚好在倒数第k个位置上。

还有一些相关题目比较有趣,类似的思路。

(1)求链表的中间结点

答:设置两个指针,移动快慢不同。快指针每次移动2步,慢指针每次移动1步。两者同时从头结点开始出发。当快指针到达尾结点时,走的路程是n,由于慢指针速度是其一半,则相同时间走的路程就是n/2,恰好位于链表的中间。

如果链表中的结点为奇数,则返回中间结点;如果链表中的结点为偶数,则返回中间两个结点的任意一个(前一个)。

// fast每次走2步,终点时:偶数时自身会为null,奇数时next会为null
while( fast != NULL && fast->next != NULL)  
{    
    fast = fast->next->next;  
    slow = slow->next;  
} 

(2)判断单向链表是否有环

答:若单链表有环,则可定义两个快慢指针。和操场跑圈类似,若有环,则快指针势必会追上慢指针,此时就是有环。若没有环,快指针会走到尾结点也不会和慢指针相遇,此时无环。

代码:

bool isLoop(pNode *pHead)  
{  
    pNode *fast = pHead;  
    pNode *slow = pHead;  
    //如果无环,则fast先走到终点  
    //当链表长度为奇数时,fast->Next为空  
    //当链表长度为偶数时,fast为空  
    while( fast != NULL && fast->next != NULL)  
    {  

        fast = fast->next->next;  
        slow = slow->next;  
        // 若有环则会从此处跳出while  
        if(fast == slow)  
        {  
            break;  
        }  
    }  
    // 判断出while循环是相遇而跳,还是快指针到尾结点了退出?
    if(fast == NULL || fast->next == NULL  )  
        return false;  
    else  
        return true;  
}  

(3)若单链表有环,环的长度?

答:第一次相遇点开始,两指针同时跑,开始计数。到第二次相遇时,慢指针跑了一圈,快指针跑了2圈,停止计数。计数值即为一圈长度。

int loopLength(pNode *pHead)  
{  
    // 判断是否有环
    if(isLoop(*pHead) == false)  
        return 0;  

    pNode *fast = pHead;  
    pNode *slow = pHead;  
    int length = 0;  
    bool begin = false;  
    bool agian = false;  
    while( fast != NULL && fast->next != NULL)  
    {  
        fast = fast->next->next;  
        slow = slow->next;  
        //第2次相遇,停止计数,出循环  
        if(fast == slow && agian == true)  
            break;  
        //第一次相遇,开始计数  
        if(fast == slow && agian == false)  
        {             
            begin = true;  
            agian = true;  
        }    
        //计数  
        if(begin == true)  
            ++length;          
    } 

    return length;  
}  

(4)如何找到环的入口点?

答:由归纳推理可得,从第一次的相遇点处,到环入口的距离,等于头结点到环入口的距离。

这里写图片描述

假设头结点到入口处为L1,相遇点顺时针入口点为L3,入口点到相遇点为L2。则当两指针相遇时,慢指针走了(L1+L2),快指针速度是2倍,应该走的是2(L1+L2)。再根据图上关系,快指针走的还可以表示为(L1+L2+L3+L2),则

2(l1+l2)=l1+l2+l3+l2l1=l3

所以当第一次相遇时,从相遇点和头结点分别设置两个移动步长均为1的指针,当相遇时,相遇点即为入口点。

Node* findLoopEntrance(pNode *pHead)  
{  
    pNode *fast = pHead;  
    pNode *slow = pHead;  
    while( fast != NULL && fast->next != NULL)  
    {  

        fast = fast->next->next;  
        slow = slow->next;  
        //如果有环,则fast会超过slow一圈  
        if(fast == slow)  
        {  
            break;  
        }  
    }  
    if(fast == NULL || fast->next == NULL)  
        return NULL;  
    slow = pHead;  
    while(slow != fast)  
    {  
        slow = slow->next;  
        fast = fast->next;  
    }  

    return slow;  
}  

(5)如何判断两个链表(不带环)是否相交?

答:可将其中一个链表的尾指针指向头结点。从另一个链表查找是否有环。如果有环说明相交;没有环说明不相交。


参考资料

【1】http://blog.csdn.net/thefutureisour/article/details/8174313

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值