Быстрый и медленный указатели
Обзор
Быстрый и медленный указатели - это вариант паттерна двух указателей, где один указатель движется быстрее другого. Обычно медленный указатель делает один шаг, а быстрый - два.
Этот прием особенно часто встречается в задачах на связные списки. Он позволяет находить цикл, середину списка или точку встречи без дополнительной памяти.
Идея паттерна
Представим два указателя:
slow = head;
fast = head;На каждой итерации:
slow = slow.next;
fast = fast.next.next;Если в списке есть цикл, быстрый указатель рано или поздно догонит медленный.
Если цикла нет, быстрый указатель дойдет до null.
Почему это работает
В списке без цикла быстрый указатель просто быстрее достигает конца. В списке с циклом оба указателя оказываются внутри кольца. На каждом шаге быстрый указатель сокращает расстояние до медленного на один узел, поэтому встреча неизбежна.
Для поиска середины списка идея похожая: когда быстрый указатель дошел до конца, медленный прошел примерно половину пути и стоит на середине.
Когда использовать
Паттерн хорошо подходит, когда нужно:
-
Найти цикл в связном списке.
-
Найти середину связного списка.
-
Проверить, зацикливается ли последовательность состояний.
-
Решить задачу на список с ограничением
O(1)по дополнительной памяти. -
Разделить список на две части.
В задачах на быстрый указатель важно проверять не только fast, но и
fast.next. Иначе попытка сделать fast.next.next может привести к ошибке на
последнем узле.
Итоги
Быстрый и медленный указатели позволяют получать информацию о форме линейной структуры без хеш-множества и без изменения данных.