检测单链接链表中循环的开始?

时间:2009-10-08 10:28:18

标签: loops linked-list find singly-linked-list cycle-detection

有没有办法在不超过两个指针的情况下找到链接列表中循环的开始? 我不想访问每个节点并标记它并报告第一个节点已经被看到。有没有其他方法可以做到这一点?

14 个答案:

答案 0 :(得分:118)

Step1:以通常的方式继续,您将用于查找循环,即 有两个指针,一步递增一步,两步递增,如果它们在某个时间相遇,则有一个循环。

Step2:将一个指针冻结到原来的位置,然后在一步中递增另一个指针计算你所做的步数,当它们再次相遇时,计数将给你循环的长度(这个与计算循环链接列表中的元素数量相同。)

Step3:重置指向链接列表开头的两个指针,将一个指针递增到循环次数的长度,然后启动第二个指针。在一步中递增两个指针,当它们再次相遇时,它将是循环的开始(这与从链接列表的末尾找到n th 元素相同)。

答案 1 :(得分:28)

数学证明+解决方案

Let 'k' be the number of steps from HEADER to BEGINLOOP.
Let 'm' be the number of steps from HEADER to MEETPOINT.
Let 'n' be the number of steps in the loop.
Also, consider two pointers 'P' and 'Q'. Q having 2x speed than P.

简单案例:当k < Ñ

当指针'P'处于BEGINLOOP时(即它将经过'k'步),Q将走“2k”步。因此,当P进入循环时,Q有效地从P开始'2k-k = k'步,因此,Q现在是BEGINLOOP后面的'n-k'步骤。

当P从BEGINLOOP转移到MEETPONT时,它会走“m-k”步。在那段时间里,Q将会走“2(m-k)”步。但是,既然他们遇到了,并且Q开始'n-k'步骤落后于BEGINLOOP,那么,实际上, '2(m-k) - (n-k)'应该等于'(m-k)' 所以,

=> 2m - 2k - n + k = m - k
=> 2m - n = m
=> n = m

这意味着,P和Q在等于循环中的步数(或多个一般,见下面提到的情况)的点处相遇。现在,在MEETPOINT,P和Q都是'n-(m-k)'后面的步骤,即'k'步后面,我们看到n = m。 所以,如果我们再次从HEADER开始P,从MEETPOINT开始Q,但这次的速度等于P,P和Q现在只在BEGINLOOP开会。

一般情况:比如说,k = nX + Y,Y&lt; Ñ (因此,k%n = Y)

当指针'P'处于BEGINLOOP时(即它将经过'k'步),Q将走“2k”步。因此,当P进入循环时,Q有效地从P开始'2k-k = k'步。但是,请注意'k'大于'n',这意味着Q将进行多轮循环。因此,有效地,Q现在是'n-(k%n)'步骤落后于BEGINLOOP。

当P从BEGINLOOP转移到MEETPOINT时,它会走“m-k”步。 (因此,有效的是,MEETPOINT现在将在'(m-k)%n'步前超过BEGINLOOP。)在那个时候,Q将会走“2(m-k)”步。但是,既然他们遇到了,并且Q开始'n-(k%n)'步骤落后于BEGINLOOP,那么,实际上,Q的新位置(即'(2(mk) - (nk /%n))%n '来自BEGINLOOP)应该等于P的新位置(来自BEGIN LOOP的'(mk)%n')。

所以,

=> (2(m - k) - (n - k%n))%n = (m - k)%n
=> (2(m - k) - (n - k%n))%n = m%n - k%n
=> (2(m - k) - (n - Y))%n = m%n - Y   (as k%n = Y)
=> 2m%n - 2k%n - n%n + Y%n = m%n - Y
=> 2m%n - Y - 0 + Y = m%n - Y    (Y%n = Y as Y < n)
=> m%n = 0
=> 'm' should be multiple of 'n'

答案 2 :(得分:12)

首先我们试着找出,列表中是否有任何循环。如果循环存在,那么我们试图找出循环的起点。为此,我们使用两个指针,即slowPtr和fastPtr。在第一次检测(检查循环)时,fastPtr一次移动两步,但slowPtr一次向前移动一步。

enter image description here

slowPtr   1   2   3   4   5   6   7
fastPtr   1   3   5   7   9   5   7

很明显,如果列表中有任何循环,那么它们将在点(上图中的点7)处相遇,因为fastPtr指针的运行速度比其他指针快两倍。

现在,我们遇到了寻找循环起点的第二个问题。

假设他们在第7点见面(如上图所示)。然后,slowPtr退出循环并且位于列表的开头意味着在第1点但是fastPtr仍然在会合点(第7点)。现在我们比较两个指针值,如果它们相同那么它就是循环的起点,否则我们向前移动一步(这里fastPtr每次也移动一步)并再次比较直到我们找到相同的点。

slowPtr   1   2   3   4
fastPtr   7   8   9   4

现在出现一个问题,怎么可能。所以有很好的数学证明。

假设:

m => length from starting of list to starting of loop (i.e 1-2-3-4)
l => length of loop (i.e. 4-5-6-7-8-9)
k => length between starting of loop to meeting point (i.e. 4-5-6-7)

Total distance traveled by slowPtr = m + p(l) +k
where p => number of repetition of circle covered by slowPtr

Total distance traveled by fastPtr = m + q(l) + k
where q => number of repetition of circle covered by fastPtr

Since,
fastPtr running twice faster than slowPtr

Hence,
Total distance traveled by fastPtr = 2 X Total distance traveled by slowPtr
i.e
m + q(l) + k = 2 * ( m + p(l) +k )
or, m + k = q(l) - p(l)
or, m + k = (q-p) l
or, m = (q-p) l - k

So,
If slowPtr starts from beginning of list and travels "m" length then, it will reach to Point 4 (i.e. 1-2-3-4)

and
fastPtr start from Point 7 and travels " (q-p) l - k " length then, it will reach to Point 4 (i.e. 7-8-9-4),
because "(q-p) l" is a complete circle length with " (q-p) " times.

More detail here

答案 3 :(得分:4)

按照通常的方式继续查找循环。即。有两个指针,一步递增一个(slowPointer),另一个递增两步(fastPointer),如果它们在某个时间相遇,则有一个循环。

你可能已经意识到会面点是循环开头之前的步骤。

其中k是列表中非循环部分的大小。

现在慢慢走向循环

保持快速碰撞点

它们中的每一个都是从循环开始的k STep(从列表的开始起缓慢,其中在循环的头部之前快速为k步 - 绘制图片以获得清晰度)

现在以相同的速度移动它们 - 它们必须在循环开始时相遇

例如

slow=head
while (slow!=fast)
{
     slow=slow.next;
     fast=fast.next;
}

答案 4 :(得分:4)

这是在链接列表中找到循环开始的代码:

public static void findStartOfLoop(Node n) {

    Node fast, slow;
    fast = slow = n;
    do {
        fast = fast.next.next;
        slow = slow.next;
    } while (fast != slow);       

    fast = n;
    do {
        fast = fast.next;
        slow = slow.next;
    }while (fast != slow);

    System.out.println(" Start of Loop : " + fast.v);
}

答案 5 :(得分:1)

有两种方法可以在链接列表中找到循环。 1.如果有循环,则使用两个指针,一个前进一步,另一个前进两个步骤,在某一点上,两个指针都获得相同的值,并且永远不会达到null。但是如果没有循环指针在一个点上达到null并且两个指针永远不会得到相同的值。但是在这种方法中,我们可以在链接列表中得到一个循环,但是我们无法确定循环的确切位置。这也不是有效的方式。

  1. 使用哈希函数,使值应该是唯一的。如果我们试图通过异常插入副本,请注意。然后遍历每个节点并将地址推送到散列中。如果指针达到null并且散列没有异常意味着链接列表中没有循环。如果我们从散列中获得任何异常,则列表中有一个循环,这是循环开始的链接。

答案 6 :(得分:1)

我通过使用一个指针尝试了一种方法...我在几个数据集中尝试了这个方法....因为链表的每个节点的内存都按递增顺序分配,所以在遍历时来自链表头部的链表,如果节点的地址变得大于它指向的节点的地址,我们可以确定有一个循环,以及循环的开始元素。

答案 7 :(得分:1)

有关综合答案,请参阅this链接。

答案 8 :(得分:1)

我找到的最佳答案是: tianrunhe: find-loop-starting-point-in-a-circular-linked-list

  • 'm'是HEAD和START_LOOP之间的距离
  • 'L'是循环长度
  • 'd'是MEETING_POINT和START_LOOP之间的距离
  • p1在V处移动,p2在2 * V处移动   

    当2个指针相遇时:距离跑是= m + n * L -d = 2 *(m + L -d)   

      =&GT;这意味着(这里没有数学证明)如果p1从HEAD&amp; p2从MEETING_POINT&amp;他们以同样的速度前进,他们将会见@ START_LOOP

答案 9 :(得分:0)

我已经听到这个问题作为面试问题。

最优雅的解决方案是:

将两个指针放在第一个元素(称为A和B)

然后继续循环::

        
  • 将A推进到下一个元素     
  • 再次将A推进到下一个元素     
  • 将B前进到下一个元素
每次更新指针时,请检查A和B是否相同。 如果在某些时候指针A和B是相同的,那么你就有一个循环。 这种方法的问题在于,您最终可能会在循环中移动两次,但使用指针A时不会超过两次

如果你想真正找到有两个指向它的指针的元素,那就更难了。除非你愿意多次重复跟踪链表,否则我只能用两个指针说不可能做到这一点。

使用更多内存执行此操作的最有效方法是将指针放入数组中,然后对其进行排序,然后查找重复内容。

答案 10 :(得分:0)

  1. 按照通常的方式继续查找循环。即。有两个指针,一步递增一步,两步递增,如果它们在某个时间相遇,则有一个循环。

  2. 保持其中一个指针固定,获取循环中的节点总数为L。

  3. 现在从这一点开始(在循环中递增指向循环中下一个节点的第二个指针)反转链表并计算遍历的节点数,比如X.

  4. 现在使用第二个指针(循环被破坏)从循环中的同一个点传递链表并计算剩余的节点数量Y

  5. 循环在((X + Y)-L)\ 2节点之后开始。或者它从(((X + Y)-L)\ 2 + 1)节点开始。

答案 11 :(得分:0)

  1. 按照通常的方式继续查找循环。即。有两个指针,一步递增一步,两步递增,如果它们在某个时间相遇,则会有一个循环。

  2. 保持其中一个指针固定,获取循环中的节点总数为L。

  3. 现在从这一点开始(在循环中递增指向循环中下一个节点的第二个指针)反转链表并计算遍历的节点数,比如X.

  4. 现在使用第二个指针(循环被破坏)从循环中的同一个点传递链表并计算剩余的节点数量Y

  5. 循环在((X + Y)-L)\ 2节点之后开始。或者它从(((X + Y)-L)\ 2 + 1)节点开始。

答案 12 :(得分:0)

void loopstartpoint(Node head){
    Node slow = head.next;;
    Node fast = head.next.next;

    while(fast!=null && fast.next!=null){
        slow = slow.next;
        fast = fast.next.next;

        if(slow==fast){
            System.out.println("Detected loop ");
            break;
        }
    }

    slow=head;
    while(slow!=fast){
        slow= slow.next;
        fast = fast.next;
    }
    System.out.println("Starting position of loop is "+slow.data);
}

答案 13 :(得分:-2)

  1. 检测循环
  2. 将每个元素的地址复制到集合中。如果发现重复是“循环的开始”