
Учитывая связанный список с циклом, рассмотрим 2 указателя - один указатель (медленный указатель) движется со скоростью, а другой указатель (быстрый указатель) перемещается со скоростью 2 * с. Учитывая цикл в связанном списке, эти два указателя обязательно встретятся в точке внутри цикла.
Расстояние, пройденное медленным указателем до места встречи:
D(slow)=x+t1*(y+z) t1: rounds of loop taken by slow pointer till slow and fast pointer meet at a point D(fast)=x+t2*(y+z) t2 : rounds taken by fast pointer till slow and fast pointer meet at a point
И медленному, и быстрому указателю требуется одинаковое количество времени, чтобы достичь точки встречи.
Time taken by slow pointer=Time taken by fast pointer (since Time= Distance/speed) (x+t1*(loop)+y)/s =(x+ t2*(loop)+y)/2s 2(x+t1*(loop)+y)= x+t2*(loop)+y 2x-x+ (t1-t2)*(loop)+2y-y=0 x+ (t1-t2)*(loop)+y=0 since loop= y+z x+(t1-t2)*(y+z)+y=0 x+(t1-t2+1)*y+(t1-t2)*z=0 x=(t2-t1)*z +(t2-t1-1)*y -------(i)
Здесь слишком много уравнений, правда?
Рассмотрим пример:

Обозначения (D представляет x, K представляет y и M представляет z)
Здесь x = 8, y = 1 и z = 2
быстрый указатель запускается в цикле 3 раза, пока не достигнет точки встречи, следовательно, t2 = 3
медленный указатель достигает точки встречи до завершения цикла, следовательно, t1 = 0
из уравнения (i)
x=(t2-t1)*z +(t2-t1-1)*y
мы знаем, x = 8
давайте найдем RHS и проверим, если LHS = RHS?
расчет стоимости на правой стороне:
(t2-t1)*z+(t2-t1–1)*y (substituting values: t2=3, t1=0, z=2, y=1) (3-0)*2+(3 -0-1)*1 = 3*2+(2)*1 = 8
Итак, расстояние от заголовка связанного списка = расстояние от точки встречи до встречи обоих указателей. Если мы начнем указатель с заголовка связанного списка, а другой - с точки встречи, то где они встретятся?
цикл: z + y
ПРА: (t2-t1) * z + (t2-t1–1) * y (считается, что мы находимся в точке встречи)
Давайте изменим перспективу и сделаем y шагов назад, и мы достигнем начальной точки цикла, верно?
теперь правая часть RHS становится: (t2-t1) * z + (t2-t1–1) * y + y (считается, что мы находимся в начальной точке, и поскольку расстояние y уже было покрыто указателем, поэтому оно не было включено. Но теперь, когда мы меняем перспективу проблемы и считаем, что она начинается с начала цикла, нам нужно добавить y к правой стороне. Простая математика)
=> (t2-t1)*z+(t2-t1)*y
=> (t2-t1)*(y+z)
= ›(T2-t1) * (цикл)
Теперь это всегда будет в исходной позиции, независимо от того, что такое t2 и t1, поскольку цикл будет завершен полностью.
и поскольку LHS = RHS (как уже доказано)
Таким образом, легко заметить, что если мы начнем указатель с заголовка связанного списка, а другой - с точки встречи связанного списка, указатели пройдут одинаковое расстояние и встретятся в начальном узле списка.
Вот реализация описанного выше подхода на java:
public Node detectCycle(Node head) {
Node slow=head ,fast=head;
while(fast!=null && fast.next!=null && slow.next!=null)
{
fast=fast.next.next;
slow=slow.next;
if(fast==slow)
break;
}
if(fast==null || fast.next==null) //if there is no loop
return null;
else
{
slow=head; //starting the pointer from head
while(slow!=fast)
{
slow=slow.next;
fast=fast.next;
}
return slow;
}
}
Сложность по времени - O (n), а сложность по пространству - O (1)
Удачного кодирования и приятной отладки :)