Как узнать
связный список или любое правило «перейти к следующему» и вопрос про цикл, его начало или середину, часто с O(1) дополнительной памяти.
Как решать такие задачи
- Поставь оба указателя на голову списка.
- На каждом шаге двигай slow на один узел, а fast на два. Сначала проверь, что fast и fast.next существуют.
- Если fast дошёл до конца, цикла нет. В этот момент slow стоит на середине.
- Если они встретились, цикл есть. Чтобы найти его начало, верни один указатель на голову и двигай оба по одному узлу до новой встречи.
Вариант: Середина
То же, но ищем середину: быстрый указатель идёт вдвое быстрее, поэтому, когда он дошёл до конца, медленный стоит ровно посередине. Длину списка заранее знать не нужно.
- Поставь оба указателя на голову списка.
- Повторяй, пока есть fast и fast.next: двигай slow на один узел, а fast на два.
- Когда цикл закончился, slow стоит на середине. При нечётной длине fast останавливается на последнем узле, при чётной уходит на none, а slow оказывается на втором из двух средних узлов.
- Тем же приёмом список делят пополам: чтобы проверить, палиндром ли он, разверни половину после середины и сравни; чтобы отсортировать, отсортируй обе половины и слей их.
Вариант: На расстоянии n
То же, но указатели идут с одной скоростью, а быстрый стартует на n узлов раньше. Когда он дошёл до конца, второй стоит в n узлах от конца, и длину считать не нужно.
- Как узнать: позиция в связном списке считается от конца, а пройти нужно один раз или длина неизвестна.
- Поставь перед головой фиктивный узел dummy и начни с него оба указателя.
- Сдвинь fast на n шагов в одиночку. Потом двигай оба по одному, пока fast.next не станет none.
- Теперь slow стоит прямо перед n-м узлом с конца: сделай slow.next = slow.next.next и верни dummy.next.
- Так же поворачивают список на k: найди k-й узел с конца, разрежь список перед ним и прицепи старый хвост к старой голове. Сначала возьми k по модулю длины.
Где встречается
- Разложение чисел на множители. Ро-метод Полларда строит последовательность чисел, которая рано или поздно зацикливается, и находит цикл медленным и быстрым указателями, не храня сами числа. Он есть, например, в SymPy.
- Криптография. Поллард приспособил ро-метод и к дискретному логарифму, и это лучшая известная общая атака на криптографию на эллиптических кривых. Ключи длиной 256 бит выбирают как раз так, чтобы ей не хватило времени.