Поясніть, як знайти вузол запуску циклу в списку, пов'язаному з циклом?


161

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


Ще одне пояснення: marcin-chwedczuk.github.io/…
csharpfolk

Люди не дбають шукати понад два відповіді на це питання. Третя відповідь досить гарна.
displayName

Відповіді:


80

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

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

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


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

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

7
@Passionate: зробіть mu кроки від початкової точки, щоб дістатися до X_muпочатку циклу (за визначенням mu). Тоді якщо ви зробите більше кроків, де я кратна довжині циклу, ви повернетеся до початку циклу: 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.

Звичайно, ви повинні бачити, що це не обов'язково найменший i можливий. Іншими словами, черепаха та заєць, можливо, вже зустрічалися раніше. Однак, оскільки ми показуємо, що вони зустрічаються в якийсь момент хоча б раз, можна сказати, що гіпотеза правильна. Тож їм доведеться зустрітися, якщо ми перемістимо один з них 1 крок, а другий - 2 кроки за раз.

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

Цикл Початок

Як тільки черепаха і заєць зустрічаються, давайте повернемо черепаху на початок списку і збережемо зайця там, де вони зустрілися (що знаходиться в k кроках від початку циклу).

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

Доведемо цю гіпотезу.

Спочатку припустимо, що якийсь оракул говорить нам, що таке m.

Тоді, якщо ми дозволимо їм рухатися кроками m + k, черепаха повинна була б дійти до точки, з якою вони зустрілися спочатку (k кроки від початку циклу - див. На малюнку).

Раніше ми це показували m + k = (q - 2p) n.

Оскільки кроки m + k є кратною довжині циклу n, заєць у середній час пройшов би цикл (q-2p) разів і повернувся б до тієї ж точки (k кроків від початку циклу).

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

Як результат, це пояснює, що їм доведеться зустрітися на початку циклу після деякої кількості кроків вперше (дуже перший раз, тому що черепаха щойно приїхала в цикл після m кроків, і вона ніколи не може побачити зайця, який вже був у цикл).

Тепер ми знаємо, що кількість кроків, необхідних нам для їх переміщення, поки вони не зустрінуться, виявляється відстань від початку списку до початку циклу, m. Звичайно, алгоритму не потрібно знати, що таке m. Він просто перемістить і черепаху, і зайця, крок за часом, поки вони не зустрінуться. Місцем зустрічі має бути початок циклу, а кількість кроків має бути відстань (м) до початку циклу. Припускаючи, що ми знаємо довжину списку, ми можемо також обчислити довжину циклу віднімання m від довжини списку.


1
Я не думаю, що це правда, що, коли вони зустрічаються, це відправна точка, дивіться коментар нижче: stackoverflow.com/a/19209858/1744146 <br> Будь ласка, повідомте мені, якщо я помиляюся
MrA

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

1
@Gopichand Прочитайте останній пункт ще раз ... ви просто припускаєте, що є деякий m (якщо вже доведено, що існує цикл) .. але ви не знаєте значення m
Шрінат

2
Тепер це справді фантастичне пояснення. Це, мабуть, найкраще пояснення в даний час в усьому Інтернеті.
Арлен Батада

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

124

Переглянути це зображення:

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

Відстань, пройдена повільним покажчиком до зустрічі = x + y

Відстань, пройдена швидким покажчиком до зустрічі = (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 для позначення тривалості циклу. Відстань, пройдена швидким покажчиком до зустрічі = (x + y + z) + y = x + 2y + nl + z. І отримане співвідношення буде x = nl + z.
Jingguo Yao

@JingguoYao: Ось пояснення для цього випадку.
показНазви

2
ця діаграма занадто проста. швидкий вказівник може переміщатися багато разів через цикл, перш ніж повільний вказівник досягне його.
Воррен МакЕвой

70

Проста і недооцінена відповідь Старого Монка пояснює пошук циклу, коли швидкий бігун завершує лише один повний цикл. У цій відповіді я пояснюю випадок, коли швидкий бігун кілька разів запускав цикл, перш ніж повільний бігун потрапляє в цикл.


Використовуючи те саме зображення:введіть тут опис зображення

Скажімо, швидкий бігун пробіг цикл m разів перед повільною та швидкою зустріччю. Це означає що:

  • Відстань пробіг повільно: x + y
  • Відстань пробіг швидко: x + m (y + z) + y, тобто додатково y де вони зустрічаються

Оскільки швидкі пробіги з подвоєною швидкістю повільних і що вони бігають протягом того ж часу, то це означає, що якщо ми подвоїмо відстань, пробігши повільно, ми отримаємо відстань, яку пробігли швидко. Таким чином,

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

Розв’язуючи для x дає,

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

У реальному сценарії це означатиме, що x = (m - 1) проходить повний цикл + додаткова відстань z .

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


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

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, ви починаєте цикл. А для цього - це те саме, що починати з головного вузла та збиратись x вузли.
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;
anshul garg

10

Це дуже дуже просто. Можна думати з точки зору відносної швидкості. Якщо кролик рухається двома вузлами, а черепаха рухається одним вузлом, відносно черепахи кролик рухається одним вузлом (припустимо, черепаха знаходиться в спокої). Отже, якщо ми перемістимо один вузол у круговому зв'язаному списку, ми обов'язково знову зустрінемося в цьому пункті.

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


8

Фігура 1

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

Фігура 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 кроків від початку циклу, кількість кроків до початку циклу - mu, а довжина циклу - 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 не дорівнює нулю, то швидкий тепер повинен наздогнати повільний, який за часом 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 для імітації алгоритму: docs.google.com/spreadsheets/d/…
Warren MacEvoy

1
Зауважте, що якщо N <= C, ітерація припиняється після C ітерацій. У будь-якому випадку він повинен зупинитися менш ніж на N + C кроків і навряд чи зупиниться на початку циклу.
Воррен МакЕвой

2

Зменшіть проблему до проблеми циклу, а потім поверніться до початкової проблеми

Наступне пояснення мені здається більш інтуїтивним.

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

    Ми хочемо відповісти на наступне запитання "Де 2, коли 1 в А?" .

    Отже, 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 знаходиться в А. Давайте назвемо цю точку 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 ' йде від O до A , 1' ' йде від C до A і продовжує закінчувати 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 працює на 1x швидкості, B на 2x швидкості, обидва починаються на початку.

коли A досягне N [0], B має бути вже в N [m]. (Примітка: A використовує m кроків для досягнення N [0], а B має бути m кроків далі)

Тоді A виконує k більше кроків для зіткнення на B, тобто A знаходиться на N [k], B - на N [m + 2k] (Примітка: B має виконуватися на 2 крок, починаючи з 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 його також відбувається відносне відстань , яке покрите між зайцем і черепахою для них , щоб бути в змозі задовольнити . Скажімо, ця відносна відстань буде пройдена в часі, 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/3 черепахи, тож наприкінці 3 кіл черепаха зайця охопила б 1 круг і вони зустрічаються.
  2. Тепер, якщо ми розпочнемо їх m кроків до циклу, то це означає, що швидший заєць починає вперед в петлі. Отже, якщо черепаха досягає початку петлі, заєць - це m кроків вперед, а коли вони зустрінуться, це буде m кроків до початку циклу.

1

-проводиться k кроків до циклу. Ми не знаємо, що таке k, і це не потрібно з'ясовувати. Ми можемо абстрактно працювати лише з k.

- Після k кроків

----- Т на початку циклу

----- H - k кроків у цикл (він пройшов 2 к усього і, таким чином, k у цикл)

** тепер вони циклічні - k один від одного

(зауважте, що k == K == mod (циклічна величина, k) - наприклад, якщо вузол - 2 кроки в циклі 5 вузлів, це також 7, 12 або 392 кроки, тому, наскільки великим є цикл, wrt k не має фактор в.

Оскільки вони наздоганяють один одного зі швидкістю 1 крок за одиницю часу, оскільки один рухається вдвічі швидше, ніж інший, вони зустрінуться в циклі - k.

Це означає, що для початку вузла буде потрібно k вузлів, тому відстань від голови до циклу і зіткнення до циклу буде однаковою.

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

Це означає, що алгоритм:

  • від переміщення голови T = t.next і H.next.next, поки вони не стикаються (T == H) (є цикл)

// подбайте про випадок, коли k = 0 або T і H зустрілися на голівці петлі шляхом обчислення довжини петлі

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

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

--переміщення довжини вказівника кроків циклу

- перемістіть інший покажчик Н2 на голову

- переміщуйте T2 і H2 в тандемі, поки вони не зустрінуться на початку циклу

Це воно!


1

Відповідей на це вже багато, але я одного разу придумав схему для цього, яка для мене більш візуально інтуїтивна. Можливо, це може допомогти іншим людям.

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

  • Розділіть T (черепаха) на T1 (попередня петля) і T2 (в петлі). Т = черепаха, Н = заєць

  • Віднімаємо Т від Н , де вони візуально перетинаються. Те, що залишається ( H - T = 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 = k

Це, звичайно, означає, що довжина шляху, яка не є петлею, така ж, як відстань від початкової точки петлі від точки, де обидва зустрічаються.


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

Тому що заєць один раз проїхав би петлю частину, щоб уже зустріти черепаху .. Я цього не пояснив: /
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 .


Вони не обов'язково зустрічаються на початку циклу.
Воррен МакЕвой

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.