2.8 环路检测

     《程序员面试金典》(第六版)习题:仅为记录一下以加强印象,不为商业用途,如有侵权请联系删除。以下源码和解释参考了书中源码以及这里:https://github.com/alexhagiopol/cracking-the-coding-interview。源码中节点以及功能函数定义参见这里:2.1 移除重复节点

template <typename T>
SinglyLinkedNode<T>* findLoopFirst(SinglyLinkedNode<T>* head) 
{
    SinglyLinkedNode<T>* F = head;  // fast pointer
    SinglyLinkedNode<T>* S = head;  // slow pointer
    //Find the first collision point
    while (true) 
    {
        // If there is no loop, return nullptr
        if (F->getNext() == nullptr || F == nullptr) 
        {
            return nullptr;
        }
        // Advance slow and fast pointers
        S = S->getNext();
        F = F->getNext()->getNext();
        if (S == F) {
            break;
        }
    }
    // Reset slow pointer S to the head pointer
    S = head;
    // Find the start point of linked list's loop
    while (true) 
    {
        // Advance fast and slow pointers at same speed
        S = S->getNext();
        F = F->getNext();
        if (S == F) 
        {
            break;
        }
    }
    return S;
}

     这里举一个例子对算法加以说明,如图1所示。算法说明:设置两个快慢指针 f a s t fast fast s l o w slow slow,快指针每次移动两个节点,慢指针 s l o w slow slow每次移动一个节点。如果链表中有环,则当慢指针移动 n = 3 n=3 n=3步到链表中的环的起始点(黑点)时,此时快指针和慢指针都进入到环中,由于单向链表的原因,两个指针将一直在环中无限循环运行。当慢指针移动 n = 3 n=3 n=3步到链表中的环的起始点(黑点)时,快指针肯定位于环中的某个节点,此时可以认为快指针顺时针滞后于慢指针。假设这里滞后 Z Z Z个节点(注意这里是 Z Z Z个节点不是快指针移动 Z Z Z步,即此时从快指针指向的节点开始迭代取 Z Z Z n e x t next next指针可以取到黑色节点的指针),由于快指针每次移动两个节点,慢指针每次移动一个节点,实际看来每次操作快指针可以追赶慢指针一个节点。因此经过 Z Z Z次操作后快指针和慢指针将指向同一个节点。因此如果快慢指针发生碰撞,则说明链表中存在环。
     当慢指针移动 n = 3 n=3 n=3步到链表中的环的起始点,黑点,时,快指针移动了 6 = 2 ∗ n 6=2*n 6=2n步到达红色节点,也即从链表中的环的起始点,黑点,开始移动算起的第 n = 3 n=3 n=3个节点(此节点也可以说是从链表中的环的起始点,黑点,开始移动算起的第 n = 11 n=11 n=11或第 n = 19 n=19 n=19个节点,这是因为环的原因,当 n n n超过 L o o p S i z e LoopSize LoopSize时就会出现这种情况。因此这里我们用 N = n % L o o p S i z e = 3 N=n \% LoopSize=3 N=n%LoopSize=3来代替此时快指针相对于环的起点移动的节点数。这里 L o o p S i z e = 8 LoopSize =8 LoopSize=8为链表中环的节点个数)这时快指针顺时针滞后于慢指针 L o o p S i z e − N = 5 LoopSize-N=5 LoopSizeN=5个节点。上面提到过经过 L o o p S i z e − N = 5 LoopSize-N=5 LoopSizeN=5次操作后快指针和慢指针将指向同一个节点,绿点,这时绿点距离环的起点,黑点, L o o p S i z e − ( L o o p S i z e − N ) = N = 3 LoopSize-(LoopSize-N)=N=3 LoopSize(LoopSizeN)=N=3个节点,现在链表头节点距离环的起点,黑点,也是 N = n % L o o p S i z e = 3 N=n \% LoopSize=3 N=n%LoopSize=3个节点。此时将slow指针赋值为头节点的指针, f a s t fast fast指针还是指向绿点,同时 f a s t fast fast指针和 s l o w slow slow指针同时向下一个节点移动,每次移动一个节点,当两个指针指向的节点相同时,该节点即为环路的起始处。
     该算法的空间复杂度为O(1)。该算法的时间复杂度为O(n),n为链表中节点的个数。

 
图1.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqssss121dfd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值