Объясните, как работает поиск начального узла цикла в связанном списке циклов?


162

Я понимаю, что встреча Черепахи и Зайца завершает существование петли, но как перемещение черепахи в начало связанного списка при сохранении зайца в месте встречи с последующим перемещением обоих по одному шагу за раз заставляет их встретиться в начальной точке цикла?


Другое объяснение: marcin-chwedczuk.github.io/…
csharpfolk

Люди не хотели смотреть дальше первых двух ответов на этот вопрос. Третий ответ довольно хорош.
displayName

Ответы:


80

Это алгоритм Флойда для обнаружения цикла . Вы спрашиваете о второй фазе алгоритма - как только вы нашли узел, который является частью цикла, как найти начало цикла?

В первой части алгоритма Флойда заяц перемещается на два шага за каждый шаг черепахи. Если черепаха и заяц когда-либо встречаются, существует цикл, и точка встречи является частью цикла, но не обязательно первым узлом в цикле.

Когда черепаха и заяц встречаются, мы нашли наименьшее i (число шагов, предпринятых черепахой), такое, что X i = X 2i . Пусть mu представляет количество шагов, которые нужно пройти от X 0 до начала цикла, и пусть lambda представляет длину цикла. Тогда i = mu + a лямбда и 2i = mu + b лямбда, где a и b - целые числа, обозначающие, сколько раз черепаха и заяц проходили цикл. Вычитание первого уравнения из второго дает i = (ba) * lambda, так что i - целое число, кратное lambda. Следовательно, X i + mu = X mu . X i представляет собой место встречи черепахи и зайца. Если вы переместите черепаху обратно в начальный узел X0 , и пусть черепаха и заяц продолжат с той же скоростью, после дополнительных шагов mu черепаха достигнет X mu , а заяц достигнет X i + mu = X mu , поэтому вторая точка встречи обозначает начало цикл.


1
@Jim lewis Место встречи, конечно же, не будет отправной точкой, но, как я уже сказал, смещение одного из этих двух в начало связанного списка и перемещение обоих с одинаковой скоростью заставит их встретиться в начальной точке цикла.
Страстный программист

6
@Jim Lewis Было бы здорово, если бы вы могли объяснить, как i, кратное длине цикла, дает mu как расстояние между первой точкой встречи и началом цикла.
Страстный программист

7
@Passionate: Сделайте шаги мю от начальной точки, чтобы добраться до X_muначала цикла (по определению мю). Затем, если вы сделаете еще i шагов, где i кратно длине цикла, вы вернетесь обратно к началу цикла: X_mu + i= X_mu. Но сложение является коммутативным, так что это равносильно выполнению i шагов, чтобы добраться от начала до первой точки встречи X_i, а затем дополнительных шагов, чтобы вернуться к X_muначалу цикла.
Джим Льюис

2
@ankur: Место встречи - X_i, и мы показали (третий абзац в моем ответе), что я должен быть кратным длине цикла. После дополнительных шагов через точку встречи вы находитесь в X_ (i + mu). Но мы показали, что X_ (i + mu) = X_ (mu + i) = X_mu, из-за этого особого свойства i, поэтому шаги mu после точки встречи должны привести вас к X_mu, началу цикла. В основном модульная арифметика плюс коммутативное свойство сложения.
Джим Льюис

28
Я думаю, что в вашем доказательстве есть небольшая проблема. Поскольку точка встречи iнаходится в некоторой точке цикла, я думаю, что уравнение должно быть i = mu + k + a*lambdaи 2i = mu + k + b*lambdaгде kчисло шагов от начала цикла до точки встречи. Вычитание обоих уравнений дает один и тот же результат.
Иван З. Сиу

336

Позвольте мне попытаться пояснить алгоритм обнаружения циклов, который представлен на http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare своими собственными словами.

Рисование

Как это устроено

Давайте возьмем черепаху и зайца (название указателей), указывающих на начало списка с циклом, как на диаграмме выше.

Давайте предположим, что если мы будем перемещать черепаху по 1 шагу за раз, а заячь по 2 шага за раз, они в конечном итоге встретятся в определенный момент. Покажем, что в первую очередь эта гипотеза верна.

На рисунке показан список с циклом. Цикл имеет длину, nи мы изначально mотходим от цикла. Также допустим, что место встречи находится в нескольких kшагах от начала цикла, и черепаха встречается, когда черепаха предприняла iполные шаги. (К тому времени заяц предпринял бы 2iполные шаги.)

Должны соблюдаться следующие 2 условия:

1) i = m + p * n + k

2) 2i = m + q * n + k

Первый говорит, что черепаха двигает iшаги, и на этих iшагах она сначала попадает в цикл. Затем он проходит время цикла pдля некоторого положительного числа p. Наконец, он проходит kбольше узлов, пока не встретит зайца.

Аналогичное верно для зайца. Он перемещает 2iшаги, и на этих 2iшагах он сначала попадает в цикл. Затем он проходит время цикла qдля некоторого положительного числа q. Наконец, он проходит через kнесколько узлов, пока не встретит черепаху.

Поскольку заяц путешествует с удвоенной скоростью черепахи, и время одинаково для обоих, когда они достигают места встречи.

Таким образом, используя простое соотношение скорости, времени и расстояния,

2 ( m + p * n + k ) = m + q * n + k

=> 2m + 2pn + 2k = m + nq + k 

=>  m + k = ( q - 2p ) n

Среди m, n, k, p, q первые два являются свойствами данного списка. Если мы можем показать, что существует хотя бы один набор значений для k, q, p, который делает это уравнение истинным, мы покажем, что гипотеза верна.

Один такой набор решений выглядит следующим образом:

p = 0

q = m

k = m n - m

Мы можем проверить, что эти значения работают следующим образом:

m + k = ( q - 2p ) n  

=> m + mn - m = ( m - 2*0) n

=> mn = mn.

Для этого набора iесть

i = m + p n + k

=> m + 0 * n + mn - m = mn.

Конечно, вы должны видеть, что это не обязательно наименьшее из возможных. Другими словами, черепаха и заяц могли встречаться уже много раз. Однако, поскольку мы показываем, что они встречаются в какой-то момент хотя бы раз, мы можем сказать, что гипотеза верна. Так что им придется встретиться, если мы переместим одного из них на 1 шаг, а другого - на 2 шага за раз.

Теперь мы можем перейти ко второй части алгоритма, которая заключается в том, как найти начало цикла.

Начало цикла

Когда черепаха и заяц встретятся, давайте вернем черепаху в начало списка и оставим зайца там, где он встретился (это k шагов от начала цикла).

Гипотеза состоит в том, что если мы позволим им двигаться с одинаковой скоростью (1 шаг для обоих), то в первый раз, когда они снова встретятся, начнется цикл.

Давайте докажем эту гипотезу.

Давайте сначала предположим, что какой-то оракул говорит нам, что такое m.

Затем, если мы позволим им сдвинуть m + k шагов, черепаха должна прибыть в точку, с которой они встречались первоначально (k шагов от начала цикла - см. На рисунке).

Ранее мы показали это m + k = (q - 2p) n.

Поскольку m + k шагов кратно длине цикла n, заяц за это время прошел бы цикл (q-2p) раз и вернулся бы к той же точке (k шагов от начала цикла).

Теперь, вместо того, чтобы позволить им двигаться m + k шагов, если мы позволим им двигаться только m шагов, черепаха прибудет в начало цикла. Заяц будет на k шагов меньше, чем завершение (q-2p) поворотов. Поскольку он начал k шагов перед началом цикла, заяц должен был прийти к началу цикла.

В результате это объясняет, что они должны были бы встретиться в начале цикла после некоторого количества шагов в самый первый раз (самый первый раз, потому что черепаха только что прибыла в цикл после m шагов, и она никогда не могла видеть зайца, который уже был в цикл).

Теперь мы знаем, что количество шагов, которое нам нужно переместить, пока они не встретятся, оказывается расстоянием от начала списка до начала цикла, m. Конечно, алгоритм не должен знать, что такое m. Он будет просто перемещать черепаху и зайца по одному шагу за раз, пока они не встретятся. Место встречи должно быть началом цикла, а число шагов должно быть расстоянием (м) до начала цикла. Предполагая, что мы знаем длину списка, мы также можем вычислить длину цикла вычитания m из длины списка.


1
Я не думаю, что это правда, что когда они встретятся, это отправная точка, см. Комментарий ниже: stackoverflow.com/a/19209858/1744146 <br> Пожалуйста, дайте мне знать, если я ошибаюсь
MrA

Первая часть объяснения безупречна. Но у второй части есть недостаток, насколько я знаю. Вы предполагаете, что «какой-то оракул говорит m», но если m известно, у вас уже есть начало цикла. Как вы можете просто принять ответ, когда вы никогда не знаете, где начинается цикл? Пожалуйста, дайте мне знать.
Гопичанд

1
@Gopichand Прочитайте последний абзац еще раз ... вы просто предполагаете, что есть некоторое m (если уже доказано, что есть цикл) .. но вы не знаете значение m
Srinath

2
Теперь это действительно фантастическое объяснение. Это, вероятно, лучшее объяснение в настоящее время во всем Интернете.
Арлин Батада

2
Ваше уравнение m + k = (q - 2p) nможет быть дополнительно упрощено до m + k = q*n. Это связано с тем, что число петель, которые берет черепаха, всегда будет равно нулю, поскольку заяц никогда не сможет обогнать черепаху, не встретив ее. Подумай об этом.
Арпит Джайн

124

Посмотрите это изображение:

введите описание изображения здесь

Расстояние, пройденное по slowPointer до встречи = x + y

Расстояние, пройденное fastPointer до встречи = (x + y + z) + y = x + 2y + z

Так как fastPointer перемещается с удвоенной скоростью slowPointer, и время для обоих постоянное, когда достигается точка встречи.

Таким образом, используя простое соотношение скорости, времени и расстояния 2 (x + y) = x + 2y + z => x + 2y + z = 2x + 2y => x = z

Следовательно, перемещая slowPointer в начало связанного списка и заставляя slowPointer и fastPointer перемещать один узел за раз, они оба имеют одинаковое расстояние для покрытия .

Они достигнут точки, где цикл начинается в связанном списке.


10
Это не учитывает случай, когда fastPointer проходит цикл n раз, прежде чем slowPointer входит в цикл. Используйте l, чтобы обозначить длину цикла. Расстояние, пройденное fastPointer до встречи = (x + y + z) + y = x + 2y + nl + z. И полученное отношение будет x = nl + z.
Цзинго Яо

@ JingguoYao: Вот объяснение этого случая.
displayName

2
эта диаграмма слишком проста. быстрый указатель может перемещаться по циклу много раз, прежде чем медленный указатель достигнет его.
Уоррен МакЭвой

70

Простой и недооцененный ответ старого монаха объясняет нахождение цикла, когда быстрый бегун завершает только один полный цикл. В этом ответе я объясняю случай, когда быстрый бегун запускает цикл несколько раз, прежде чем медленный бегун входит в цикл.


Используя то же изображение:введите описание изображения здесь

Допустим, быстрый бегун выполнил цикл m раз, прежде чем встретиться медленно и быстро. Это означает, что:

  • Дистанция пробега медленно: х + у
  • Быстрое расстояние: x + m (y + z) + y, т. Е. Дополнительные y, где они встречаются

Поскольку быстрые пробеги с удвоенной скоростью замедления и то, что они бегали в одно и то же время, это означает, что если мы удвоим расстояние, пройденное медленным, мы получим расстояние, пройденное быстро Таким образом,

  • 2 (x + y) = x + m (y + z) + y

Решение для х дает,

x = (m - 1) (y + z) + z

В реальном сценарии это будет означать, что x = (m - 1) полный цикл проходит + дополнительное расстояние z .

Следовательно, если мы поместим один указатель в начало списка, а другой оставим в точке встречи, то перемещение их с той же скоростью приведет к тому, что указатель в цикле завершит m - 1 циклов цикла, а затем встретит другой указатель прямо в начале цикла.


7
Одно сомнение ... как гарантируется, что медленный и быстрый встретятся раньше, чем медленный займет более одного цикла?
Сирадж

4
@siraj: Медленный не будет работать в циклах, быстрый будет, так как он работает быстрее, чем медленный, и войдет в цикл раньше. И гарантировано, что они встретятся. Если медленный в j + 1 и быстрый в j, они теперь встретятся в j + 2. А если медленный в j и быстрый в j + 1, это означает, что они уже встретились в j - 1.
displayName

4
математика по-прежнему работает, если замедление идет по кругу: x + (y + z) m + y = 2 (x + (y + z) n + y), где n - число циклов вокруг медленного перед тем, как они встретятся. Это решает для (m-2n-1) (y + z) + z = x. Это означает, что начиная с места встречи, обойдите (m-2n-1) раз, вы вернулись к месту встречи, а затем идите z, вы в начале цикла. И сделать это так же, как начать с головного узла и перейти к узлам х.
mayas_mom

1
@mayas_mom: Математика может работать, но медленный никогда не сможет обойти цикл. Он всегда будет пойман либо в самом начале, либо где-то посередине.
displayName

4
x = (m - 1) (y + z) + z это можно обобщить, так как длина цикла равна y + z и поскольку речь идет только о положении. Итак, x = ((m - 1) (y + z))% (y + z)) + z, что фактически означает x = z;
Аншул Гар

10

Это очень очень просто. Вы можете думать с точки зрения относительной скорости. Если кролик перемещается на два узла, а черепаха перемещается на один узел, то по отношению к черепахе кролик перемещается на один узел (предположим, черепаха в покое). Итак, если мы переместим один узел в круговом связанном списке, мы обязательно встретимся в этот момент снова.

После нахождения связанной точки в круговом связанном списке теперь проблема сводится к поиску точки пересечения задачи двух связанных списков.


8

фигура 1

Во время первого столкновения черепаха переместилась на m + k шагов, как показано выше. Заяц движется в два раза быстрее черепахи, то есть заяц прошел 2 (m + k) шага. Из этих простых фактов мы можем вывести следующий график.

фигура 1

В этот момент мы возвращаем черепаху к началу и заявляем, что и заяц, и черепаха должны двигаться по одному шагу за раз. По определению, после m шагов черепаха будет в начале цикла. Где зайца будет?

Заяц также будет в начале цикла. Это видно из второго графика: когда черепаха была возвращена к началу, займ был на k шагов в свой последний цикл. После m шагов заяц завершит еще один цикл и столкнется с черепахой.


@WarrenMacEvoy Ни в коем случае я не предлагал им встретиться в начальной точке. Они встречаются снова в начале цикла, как ясно показывают цифры.
skedastik

5

Подходить:

Есть два указателя:

  • Медленный указатель, который перемещает один узел за раз.
  • Быстрый указатель, который перемещает два узла одновременно.

Если два указателя встречаются, это доказывает, что есть цикл. После того, как они встретились, один из узлов будет указывать на голову, а затем оба будут проходить по одному узлу за раз. Они встретятся в начале цикла.

Обоснование: Когда два человека идут по круговой дорожке, один из них с удвоенной скоростью, другой, где они встречаются? Именно там, где они начали.

Теперь предположим, что быстрый бегун имеет быстрый старт kшагов на nкруге. где они встретятся? Именно на n-kшагах. Когда медленный бегун покрыл (n-k)шаги, быстрый бегун покрыл бы k+2(n-k)шаги. ( то есть k+2n-2kшаги, то есть 2n-kшаги ). т.е. (n-k)шаги (путь круговой, и нас не волнует количество раундов, после которых они встречаются; нас просто интересует положение, где они встречаются).

Теперь, как быстрый бегун получил быстрый старт kшагов во-первых? Потому что медленному бегуну понадобилось столько шагов, чтобы достичь начала цикла. Таким образом, начало цикла составляет k шагов от головного узла.

Примечание . Узел, где встречаются оба указателя, находится в нескольких kшагах от начала цикла (внутри цикла), а головной узел также находится в нескольких kшагах от начала цикла. Поэтому, когда у нас есть движение указателя с одинаковым шагом в 1 шаг от бота, эти узлы встретятся в начале цикла.

Я считаю, что это просто. Пожалуйста, дайте мне знать, если какая-то часть неоднозначна.


4
Пожалуйста,
оставьте

4

Итак, давайте предположим, что заяц и черепаха встречаются в точке, которая находится в k шагах от начала цикла, число шагов до начала цикла равно мю, а длина цикла равна L.

Так что теперь на месте встречи ->

Расстояние, пройденное черепахой = mu + a * L + k - Уравнение 1

(Шаги, предпринятые для достижения начала цикла + шаги, предпринятые для охвата «и» итераций цикла + k шагов от начала цикла) (где a - некоторая положительная постоянная)

Расстояние, пройденное зайцем = mu + b * L + k - уравнение 2

(Шаги, предпринятые для достижения начала цикла + шаги, предпринятые для охвата «b» итераций цикла + k шагов от начала цикла) (где b - некоторая положительная постоянная, а b> = a)

Таким образом, дополнительное расстояние, пройденное зайцем, составляет = Уравнение 2 - Уравнение 1 = (ba) * L

Обратите внимание, что это расстояние также равно расстоянию черепахи от начальной точки, поскольку заяц движется в 2 раза быстрее, чем черепаха. Это может быть приравнено к 'mu + k', которое также является расстоянием до точки встречи от начала, если мы не включаем многократные обходы цикла.

Таким образом, mu + k = (ba) * L

Таким образом, некоторые шаги с этой точки могут привести к началу цикла (поскольку k шагов от начала цикла уже были предприняты для достижения точки встречи). Это может произойти в том же цикле или любом из последующих циклов. Таким образом, теперь, если мы переместим черепаху в начало связанного списка, потребуется несколько шагов, чтобы достичь начальной точки цикла, а заяц предпримет несколько шагов, чтобы также достичь начала цикла, и, таким образом, они оба встретятся в отправная точка цикла.

PS Честно говоря, у меня был тот же вопрос, что и у оригинального плаката, и я прочитал первый ответ, они кое-что прояснили, но я не смог четко получить конечный результат, поэтому я попытался сделать это по-своему и легче понять.


они обычно не встречаются в начале цикла
Уоррен МакЭвой

3

введите описание изображения здесь кредит изображения

Вызовите расстояние - количество ссылок, за которыми следует указатель, и время, которое занимает алгоритм для перемещения медленного указателя на одну ссылку и быстрого указателя на две ссылки. Перед циклом длиной C имеется N узлов, помеченных смещением цикла от k = 0 до C-1.

Чтобы достичь начала цикла, медленный занимает N времени и расстояния. Это означает, что быстро занимает N расстояние в цикле (N, чтобы добраться, N, чтобы вращаться). Таким образом, в момент времени N медленное значение равно смещению цикла k = 0, а быстрое значение равно смещению цикла k = N mod C.

Если N mod C равно нулю, медленные и быстрые теперь совпадают, и цикл находится в момент времени N и позиция цикла k = 0.

Если N mod C не равно нулю, тогда fast теперь должен догнать медленный, который в момент N является C- (N mod C) расстоянием позади в цикле.

Поскольку быстрые перемещения 2 для каждого 1 медленного, сокращая расстояние на 1 на каждой итерации, это занимает столько же дополнительного времени, сколько расстояние между быстрым и медленным в момент N, который равен C- (N mod C). Поскольку замедление движется от смещения 0, это также смещение, где они встречаются.

Таким образом, если N mod C равно нулю, фаза 1 останавливается после N итераций в начале цикла. В противном случае фаза 1 останавливается после итераций N + C- (N mod C) со смещением C- (N mod C) в цикл.

// C++ pseudocode, end() is one after last element.

int t = 0;
T *fast = begin();
T *slow = begin();
if (fast == end()) return [N=0,C=0];
for (;;) {
    t += 1;
    fast = next(fast);
    if (fast == end()) return [N=(2*t-1),C=0];
    fast = next(fast);
    if (fast == end()) return [N=(2*t),C=0];
    slow = next(slow);
    if (*fast == *slow) break;
}

Итак, фаза 2: замедление занимает еще N шагов, чтобы добраться до цикла, и в этот момент быстрое (теперь перемещается 1 за шаг) находится в (C- (N mod C) + N) mod C = 0. Таким образом, они встречаются в начале цикла после фазы 2.

int N = 0;
slow = begin();
for (;;) {
    if (*fast == *slow) break;
    fast = next(fast);
    slow = next(slow);
    N += 1;
}

Для полноты, фаза 3 вычисляет длину цикла, продвигаясь еще раз через цикл:

int C = 0;
for (;;) {
    fast = next(fast);
    C += 1;
    if (fast == slow) break;
}

Ссылка на Google Doc
Уоррен МакЭвой

1
Обратите внимание, что если N <= C, итерация останавливается после итераций C. В любом случае он должен остановиться менее чем за N + C шагов и вряд ли остановится в начале цикла.
Уоррен МакЭвой

2

Сведите проблему к проблеме цикла, затем вернитесь к исходной проблеме.

Я считаю следующее объяснение более интуитивным.

  1. Возьмите два указателя ( 1 = черепаха и 2 = заяц), которые начинаются с головы ( O ), 1 имеет длину шага 1 , 2 имеет длину шага 2 . Подумайте о том моменте, когда 1 достигает начального узла этого цикла ( A ).

    Мы хотим ответить на следующий вопрос: «Где 2, когда 1 в A?» ,

    Итак, OA = aнатуральное число ( a >= 0). Но это можно записать следующим образом:, a = k*n + bгде a, k, n, b are natural numbers:

    • n = длина цикла
    • k >= 0 = постоянная
    • 0 <= b <= n-1

    Это значит что b = a % n.

    Например: если a = 20и n = 8=> k = 2и b = 4потому что 20 = 2*8 + 4.

    Расстояние, пройденное на 1 есть d = OA = a = k*n + b. Но в то же время 2 обложки D = 2*d = d + d = OA + d = OA + k*n + b. Это означает, что когда 2 находится в A, оно должно покрывать k*n + b. Как вы можете видеть, kэто число кругов, но после этих кругов 2 будет b далеко от A. Итак, мы нашли, где 2 - это когда 1 находится в A. Давайте назовем ту точку B, где AB = b.

    введите описание изображения здесь

  2. Теперь мы сводим задачу к кругу. Вопрос "Где место встречи?" , Где это С ?

    введите описание изображения здесь

    На каждом шаге 2 уменьшает расстояние от 1 с 1(скажем, метр), потому что 1 продвигается дальше от 2 с 1, но в то же время 2 приближается к 1 на 2.

    Таким образом, пересечение будет, когда расстояние между 1 и 2 будет равно нулю. Это означает, что 2 уменьшает n - bрасстояние. Для этого 1 сделает n - bшаги, а 2 сделает 2*(n - b)шаги.

    Таким образом, точка пересечения будет находиться n - bдалеко от A (по часовой стрелке), потому что это расстояние, пройденное 1, пока не встретит 2 . => расстояние между C и A есть CA = b, потому что AC = AB + BC = n - bи CA = n - AC. Не думайте, что AC = CA, поскольку ACрасстояние не является тривиальным математическим расстоянием, это число шагов между A и C (где A - начальная точка, а C - конечная точка).

  3. Теперь вернемся к исходной схеме.

    Мы знаем это a = k*n + bи CA = b.

    Мы можем взять 2 новых указателя 1 ' и 1' ' , где 1' начинается с головы ( O ), а 1 '' начинается с точки пересечения ( C ).

    В то время как 1 ' идет от О до А , 1' ' идет от С до А и продолжает заканчивать kкруги. Таким образом, точка пересечения .

    введите описание изображения здесь

    введите описание изображения здесь


2

введите описание изображения здесь

Если указатели встречаются в точке P, как показано на рисунке, расстояние Z + Y является точкой P, а X + Y также является точкой P, что означает Z = X. Вот почему продолжайте перемещать один указатель из P и перемещать другой от начала (S) до их встречи, что означает перемещение на одинаковое расстояние (Z или X) к той же точке M (расстояние Z от P и X от S) будет начало цикла. Просто!


1

С учетом всего вышеприведенного анализа, если вы учитесь на личном опыте, я попытался написать краткий анализ и пример, который поможет объяснить математику, которую пытались объяснить все остальные. Вот так!

Анализ:

Если у нас есть два указателя, один быстрее другого, и они движутся вместе, они в конечном итоге встретятся снова, чтобы указать цикл или ноль, чтобы указать отсутствие цикла.

Чтобы найти начальную точку цикла, позвольте ...

  1. m расстояние от головы до начала цикла;

  2. d быть числом узлов в цикле;

  3. p1 скорость медленного указателя;

  4. p2быть скоростью более быстрого указателя, например. 2 означает шаги через два узла одновременно.

    Соблюдайте следующие итерации:

 m = 0, d = 10:
 p1 = 1:  0  1  2  3  4  5  6  7  8  9 10 // 0 would the start of the cycle
 p2 = 2:  0  2  4  6  8 10 12 14 16 18 20

 m = 1, d = 10:
 p1 = 1: -1  0  1  2  3  4  5  6  7  8  9
 p2 = 2: -1  1  3  5  7  9 11 13 15 17 19

 m = 2, d = 10:
 p1 = 1: -2 -1  0  1  2  3  4  5  6  7  8
 p2 = 2: -2  0  2  4  6  8 10 12 14 16 18

Из приведенных выше примеров данных мы можем легко обнаружить, что всякий раз, когда более быстрые и медленные указатели встречаются, они находятся в нескольких mшагах от начала цикла. Чтобы решить эту проблему, поместите более быстрый указатель обратно в головку и установите его скорость равной скорости более медленного указателя. Когда они встречаются снова, узел является началом цикла.


1

скажем так,

N[0] is the node of start of the loop, 
m is the number of steps from beginning to N[0].

у нас есть 2 указателя A и B, A работает с 1-кратной скоростью, B с 2-кратной скоростью, оба начинаются с начала.

когда A достигает N [0], B уже должен быть в N [m]. (Примечание: A использует m шагов для достижения N [0], а B должно быть m шагов дальше)

Затем A выполняет еще k шагов, чтобы столкнуться с B, т. Е. A находится в N [k], B находится в N [m + 2k] (Примечание: B должно пройти 2k шагов, начиная с N [m])

Столкновение B в N [k] и N [m + 2k] соответственно означает k = m + 2k, следовательно, k = -m

Таким образом, чтобы вернуться к N [0] из N [k], нам нужно еще m шагов.

Проще говоря, нам нужно выполнить еще m шагов после того, как мы нашли узел столкновения. У нас может быть указатель для запуска с начала и указатель от узла столкновения, они встретятся в N [0] после m шагов.

Следовательно, псевдокод выглядит следующим образом:

1) A increase 1 step per loop
2) B increase 2 steps per loop
3) if A & B are the same node, cycle found, then go to 5
4) repeat from 1
5) A reset to head
6) A increase 1 step per loop
7) B increase 1 step per loop
8) if A & B are the same node, start of the cycle found
9) repeat from 6

1

Я не думаю, что это правда, что когда они встречаются, это отправная точка. Но да, если другой указатель (F) был в точке встречи раньше, чем этот указатель будет в конце цикла вместо начала цикла и указатель (S), который начался с начала списка, будет в конечном итоге в начале цикла. например:

1->2->3->4->5->6->7->8->9->10->11->12->13->14->15->16->17->18->19->20->21->22->23->24->8

Meet at :16

Start at :8

public Node meetNodeInLoop(){

    Node fast=head;
    Node slow=head;

    fast=fast.next.next;
    slow=slow.next;

    while(fast!=slow){

        fast=fast.next;
        fast=fast.next;

        if(fast==slow) break; 

        slow=slow.next;
    }

    return fast;

}

public Node startOfLoop(Node meet){

    Node slow=head;
    Node fast=meet;

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

    return slow;
}

1

Простое объяснение с использованием идеи относительной скорости, преподаваемой в старшей школе, - физика 101 / Кинематика лекций.

Круг в LinkedList

  1. Предположим, расстояние от начала связанного списка до начала круга равно xпрыжкам. Назовем начало круга точкой X(в заглавных буквах - см. Рисунок выше). Также предположим, что общий размер круга составляет N прыжков.

  2. Скорость зайца = 2 * Скорость черепахи. Так что есть 1 hops/secи 2 hops/secсоответственно

  3. Когда черепаха достигает начала круга X, заяц должен xпрыгать дальше в точке Yна рисунке. (Потому что заяц преодолел вдвое большее расстояние, чем черепаха).

  4. Таким образом, длина оставшейся дуги по часовой стрелке от X до Y будет N-x. Это также оказывается относительным расстоянием, которое должно быть пройдено между зайцем и черепахой, чтобы они могли встретиться . Допустим, это относительное расстояние будет пройдено во времени, t_mт.е. во времени встречи. Относительная скорость (2 hops/sec - 1 hops/sec)есть 1 hops/sec. Таким образом, используя, относительное расстояние = относительная скорость X время, мы получаем, t= N-xсек. Так что потребуется, N-xчтобы добраться до места встречи черепахи и зайца.

  5. Теперь через N-xсекунду и со 1 hops/secскоростью черепаха, которая была ранее в точке, Xбудет покрывать Nx прыжков, чтобы достичь места встречи M. Таким образом, это означает, что точка встречи Mнаходится в N-xпрыжках против часовой стрелки от X= (что также подразумевает) => что есть xрасстояние, оставшееся от точки Mдо по Xчасовой стрелке.

  6. Но xэто также расстояние до точки достижения Xот начала связанного списка.

  7. Теперь нас не волнует, какое количество прыжков xсоответствует. Если мы поместим одну черепаху в начале LinkedList и одну черепаху в место встречи Mи позволим им прыгать / ходить, тогда они встретятся в точке X, которая является точкой (или узлом), которая нам нужна.


1

Работа с диаграммой поможет. Я пытаюсь объяснить проблему без уравнений.

  1. Если мы допустим, что заяц и черепаха бегут по кругу, а заяц бегает дважды черепахой, то в конце одного круга у зайца-черепахи будет половина. В конце двух кругов у черепахи-зайца было бы 1 круг, и они оба встретились. Это относится ко всем скоростям, как если бы заяц бегал три раза, за 1 заяц равнялся 1/3 черепахи, поэтому в конце 3 круга заяц-черепаха мог бы преодолеть 1 круг, и они встретились.
  2. Теперь, если мы запустим их за m шагов до цикла, то это означает, что более быстрый заяц стартует вперед в цикле. Таким образом, если черепаха достигает начала цикла, заяц находится на m шагов впереди цикла, а когда они встретятся, до начала цикла будет m шагов.

1

Есть k шагов до цикла. Мы не знаем, что такое k, и нам не нужно это выяснять. Мы можем работать абстрактно только с k.

- после k шагов

----- T в начале цикла

----- H это k шагов в цикле (он пошел 2k всего и, таким образом, k в цикл)

** они теперь петлеобразные - к друг от друга

(обратите внимание, что k == K == mod (loopsize, k) - например, если узел проходит 2 шага в цикле из 5 узлов, он также включает 7, 12 или 392 шага, поэтому, насколько велик цикл по отношению к k, не имеет значения фактор в.

Так как они догоняют друг друга со скоростью 1 шаг в единицу времени, потому что один движется в два раза быстрее другого, они встретятся с размером петли - k.

Это означает, что потребуется k узлов, чтобы достичь начала цикла, и, таким образом, расстояние от начала до начала цикла и столкновения до начала цикла одинаковы.

Так что теперь после первого столкновения верните Т обратно в голову. T и H встретятся при старте, если вы двигаетесь со скоростью 1 каждый. (по k шагов для обоих)

Это означает, что алгоритм:

  • от головы двигайте T = t.next и H.next.next, пока они не столкнутся (T == H) (есть цикл)

// заботиться о случае, когда k = 0 или T и H встретились в начале цикла, вычисляя длину цикла

- рассчитать длину цикла, перемещая T или H вокруг него с помощью счетчика

- переместить указатель T2 в начало списка

- указатель длины цикла шагов

- переместить другой указатель H2 на голову

- перемещать Т2 и Н2 в тандеме, пока они не встретятся в начале цикла

Это оно!


1

На это уже есть множество ответов, но однажды я придумал диаграмму для этого, которая является более наглядной для меня. Возможно, это может помочь другим людям.

Основными аха-моментами для меня были:

  • Разделите T (черепаха) на T1 (предварительный цикл) и T2 (внутри цикл). T = черепаха, H = заяц

  • Вычтите T из H , где они визуально перекрываются. То , что остается ( H - T = H» ) равна Т .

  • Оставшаяся математика довольно проста. Из H вычтите, где T визуально перекрывается

-1

Я знаю, что уже есть принятый ответ на эту проблему, но я все еще попытаюсь ответить в жидкой форме. Предполагать :

The length of the Path is 'X+B' where 'B' is the length of the looped path and X of the non looped path. 
    Speed of tortoise : v
    Speed of hare     : 2*v 
    Point where both meet is at a distance 'x + b - k' from the starting point.

Теперь, пусть заяц и черепаха встретятся через время «т» от начала.

Замечания:

Если расстояние, пройденное черепахой = v * t = x + (bk) (скажем)

Затем расстояние, пройденное зайцем = 2 * v * t = x + (b - k) + b (поскольку заяц уже однажды прошел по зацикленной части)

Время встреч там одинаковое.

=> x + 2 * b - k = 2 * (x + b - k)

=> х = к

Это, конечно, означает, что длина пути, который не является зацикленным, равна расстоянию начальной точки цикла от точки, где встречаются оба.


Вы не можете предположить, что черепаха путешествовала точно x + bk к тому времени, когда они встречаются. Кроме того, я не понимаю, как вы получили х + 2 * бк за дистанцию ​​зайца.
Plumenator

Потому что заяц должен был пройти через зацикленную часть, чтобы встретиться с черепахой. Я не объяснил это там: /
n0nChun

-1

На самом деле легко доказать, что они оба встретятся в начальной точке, если вы рассмотрите математику за точкой встречи.
Во-первых, пусть m обозначает начальную точку цикла в связанном списке, а n обозначает длину цикла. Тогда для зайца и черепахи встретиться имеем:

( 2*t - m )%n = (t - m) %n, where t = time (at t = 0 , both are at the start)

Заявив это более математически:

(2*t - m - (t - m) ) = 0 modulo n , which implies , t = 0 modulo n 

поэтому они встретятся в момент времени t, который должен быть кратным длине цикла. Это означает, что они встречаются в месте, которое есть (t-m) modulo n = (0-m) modulo n = (-m) modulo n.

Итак, теперь возвращаясь к вопросу, если вы переместите один указатель из начала связанного списка, а другой из точки пересечения, после m шагов мы получим, что заяц (который движется внутри цикла) придет к точке, которая ((-m) + m) modulo n = 0 modulo nкоторая является ничем иным, как начальной точкой цикла. Итак, мы можем видеть, что после m шагов наступает начало цикла, и черепаха встретит его там, поскольку будет проходить m шагов от начала связанного списка.

В качестве примечания, мы также можем рассчитать время их пересечения следующим образом: Условие t = 0 modulo nговорит нам, что они встретятся за время, кратное длине цикла, а также t должно быть больше, чем m, как они встретились бы в цикл . Таким образом, время будет равно первому кратному n, которое больше m .


Они не обязательно встречаются в начале цикла.
Уоррен МакЭвой

-1

Предположим, что ваши указатели встречаются на пересечении точек y и z.

n и m числа циклов быстрее и медленнее указатель занимает соответственно до встречи.

Обратитесь к изображению для остальной части доказательства. Найти начальную точку цикла в связанном списке

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.