Подтвердить что ты не робот

Доказательство обнаружения начала цикла в связанном списке

Из нескольких сообщений внутри stackoverflow и снаружи я узнал, как определять циклы в связанном списке, длительность цикла. Я также нашел метод определения начала цикла.

Ниже приведены шаги для ссылки.

Обнаружение цикла:

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

Поиск длины цикла:

Держите один указатель фиксированным в точке встречи, а затем увеличивайте его до тех пор, пока он не станет таким же. Увеличьте счетчик, когда вы идете вперед, а значение счетчика на встречном будет длиной цикла.

Найти начало цикла

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

Может ли кто-нибудь предоставить математическое доказательство того, почему этот метод работает?

4b9b3361

Ответ 1

Вы можете упростить доказательство "Найти начало цикла", если вы не используете точку встречи.
Пусть второй указатель начинается не в "точке встречи", а M шаг впереди первого указателя. (Где M - длина цикла.)

Таким образом, доказательство довольно очевидно. Когда первый указатель достигнет начала цикла, второй будет ровно M шагом вперед: также в начале цикла.

Ответ 2

Если вы представляете список указателем на его первый node (список)

enter image description here

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

  • Объявите два указателя (pFast) и ( pSlow).
  • Сделайте pSlow и pFast в списке .
  • До (pSlow), ( pFast) или оба указывают на NULL:
    1. enter image description here
    2. enter image description here
    3. Если enter image description here, то STOP как цикл только что был найден.
Если эта точка достигнута (один или оба указателя равны NULL), то в списке нет циклов.

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

enter image description here

Обратите внимание, что каждый node, кроме указателя на начало цикла, помечен номером его цели минус единица. Таким образом, если один node помечен i, и это не начало цикла, то он обозначается как следующий элемент node с меткой i-1.

Алгоритм, описанный выше, может быть описан как цикл, поскольку его основной шаг (3) представляет собой набор подэтапов, которые повторяются до тех пор, пока условие выхода не будет выполнено. Это заставляет нас представлять pFast и pSlow в функции номера итерации при выполнении алгоритма (t).

enter image description here

Если в списке нет циклов, позиции указателей будут описаны в функции t как:

enter image description here

Однако существует node, где начинается цикл, и эта функция перестает описывать эволюцию указателей. Предполагая, что этот указатель помечен m, что цикл содержит узлы (то есть enter image description here и enter image description here) и устанавливает t = 0 как значение итерации, когда enter image description here затем:

enter image description here

Если одного указателя действительно достаточно для обнаружения циклов с использованием описанного алгоритма, то он должен существовать, по крайней мере, для решения уравнения enter image description here.

Это уравнение истинно тогда и только тогда, когда существует значение t, которое делает:

enter image description here

Это заканчивается функцией enter image description here, которая генерирует значения t, которые являются действительными решениями уравнения, описанного выше:

enter image description here

enter image description here

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

Ответ 3

Расстояние, пройденное 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 перемещать один node за раз, оба они имеют одинаковое расстояние для покрытия.

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

Диаграмма доказательства

Ответ 4

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

enter image description here

Запустите F как Fast ans S так же медленно, как в конце, на 9. Теперь, когда я начинаю S с начала снова, чем он достигнет в начале цикла, т.е. 7, но f будет в 16..

Пожалуйста, дайте мне знать, если я ошибаюсь

Ответ 5

Вторая часть, заявляющая, что "чтобы найти начало цикла, просто переместите один указатель назад в начало списка, а затем итерации до тех пор, пока они не совпадут", неверно!

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

Попробуйте со связанным списком с 11 элементами, где 11-й указывает на 7-й:

1 1 → 3 2 → 5 3 → 7 4 → 9 5 → 11 6 → 8 7 → 10 8 → 7 9 → 9 9

- обнаружен цикл

Переместите один назад, чтобы начать и увеличить их: 1 9 → 2 10 → 3 11 → 4 7 → 5 8 → 6 9 → 7 10 → 8 11 → 9 7 → 10 8 → 11 9 → 7 10 → 8 11 → ...

Ответ 6

/* Algorithm : P2 is moving through the list twice as fast as P1.
   If the list is circular, it will eventually get around P1 and meet */

public boolean hasCycle()
{
  DoubleNode p1,p2;
  p1=p2=firstNode;    //Start with the first loop

  try
  {
    while (p2 != null)     //If p2 reaches end of linked list, no cycle exists
    {
        p1=p1.next;   //Move to next
        p2=p2.next.next; //Move to 2 steps next
        if(p1==p2)
           return true;     //p1 and p2 met, so this means that there is a cycle
    }
  }
  catch(NullPointerException npe)
  {
      //This means that p2 could not move forward
      return false;
  }

  return false;
}