Spiegare come funziona il nodo di inizio ciclo di ricerca nell'elenco dei cicli collegati?


162

Capisco che l'incontro tra Tartaruga e Lepre conclude l'esistenza del ciclo, ma in che modo spostare la tartaruga all'inizio dell'elenco collegato mantenendo la lepre nel punto di incontro, seguito dallo spostamento di un passo alla volta, li fa incontrare al punto di partenza del ciclo?



Le persone non si sono preoccupate di guardare oltre le prime due risposte a questa domanda. La terza risposta è abbastanza buona.
displayName

Risposte:


80

Questo è l'algoritmo di Floyd per il rilevamento dei cicli . Stai chiedendo della seconda fase dell'algoritmo: una volta trovato un nodo che fa parte di un ciclo, come si trova l' inizio del ciclo?

Nella prima parte dell'algoritmo di Floyd, la lepre muove due passi per ogni passo della tartaruga. Se la tartaruga e la lepre si incontrano mai, c'è un ciclo e il punto di incontro fa parte del ciclo, ma non necessariamente il primo nodo del ciclo.

Quando la tartaruga e la lepre si incontrano, abbiamo trovato il più piccolo i (il numero di passi compiuti dalla tartaruga) tale che X i = X 2i . Lascia che mu rappresenti il ​​numero di passaggi da X 0 all'inizio del ciclo e che lambda rappresenti la lunghezza del ciclo. Quindi i = mu + a lambda e 2i = mu + b lambda, dove aeb sono numeri interi che indicano quante volte la tartaruga e la lepre hanno fatto il giro del ciclo. Sottraendo la prima equazione dalla seconda si ottiene i = (ba) * lambda, quindi i è un multiplo intero di lambda. Pertanto, X i + mu = X mu . X i rappresenta il punto d'incontro di tartaruga e lepre. Se si sposta la tartaruga sul nodo iniziale X0 e lascia che la tartaruga e la lepre continuino alla stessa velocità, dopo mu ulteriori passi la tartaruga avrà raggiunto X mu e la lepre avrà raggiunto X i + mu = X mu , quindi il secondo punto di incontro indica l'inizio del ciclo.


1
@Jim lewis Il punto di incontro non sarà ovviamente un punto di partenza, ma come ho detto, spostare uno di quei due all'inizio dell'elenco collegato e spostarli entrambi alla stessa velocità li farà incontrare al punto di partenza del ciclo.
Appassionato programmatore

6
@Jim Lewis Sarebbe bello se tu potessi spiegare come avere un multiplo della lunghezza del loop si traduca in mu come distanza tra il primo punto di incontro e l'inizio del loop.
Appassionato programmatore

7
@Passionato: fai i passi di mu dal punto di partenza per arrivare X_muall'inizio del ciclo (per definizione di mu). Quindi se fai più passi, dove sono un multiplo della lunghezza del ciclo, finirai indietro all'inizio del ciclo: X_mu + i= X_mu. Ma l'aggiunta è commutativa, quindi equivale a fare i passi per arrivare dall'inizio al primo punto d'incontro X_i, quindi mu ulteriori passi per tornare X_muall'inizio del ciclo.
Jim Lewis,

2
@ankur: Il punto di incontro è X_i e abbiamo mostrato (terzo paragrafo nella mia risposta) che devo essere un multiplo della lunghezza del loop. Dopo aver compiuto ulteriori passi oltre il punto di incontro, ora ci si trova in X_ (i + mu). Ma abbiamo dimostrato che X_ (i + mu) = X_ (mu + i) = X_mu, a causa di questa proprietà speciale di i, quindi mu passi oltre il punto di incontro che ti porta a X_mu, l'inizio del ciclo. Fondamentalmente modulare aritmetica, oltre alla proprietà commutativa di addizione.
Jim Lewis,

28
Penso che ci sia un piccolo problema nella tua prova. Poiché il punto di incontro si itrova ad un certo punto del ciclo, penso che dovrebbe essere l'equazione i = mu + k + a*lambdae 2i = mu + k + b*lambda, dov'è kil numero di passi dall'inizio del ciclo al punto di incontro. Sottraendo entrambe le equazioni si ottiene lo stesso risultato.
Ivan Z. Siu,

336

Vorrei provare a chiarire l'algoritmo di rilevazione del ciclo fornito su http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare con parole mie.

disegno

Come funziona

Prendiamo una tartaruga e una lepre (nome dei puntatori) che indicano l'inizio dell'elenco con un ciclo, come nel diagramma sopra.

Ipotizziamo che se spostiamo la tartaruga di 1 passo alla volta e lepre 2 passi alla volta, alla fine si incontreranno. Mostriamo che prima di tutto questa ipotesi è vera.

La figura illustra un elenco con un ciclo. Il ciclo ha una lunghezza di ne siamo inizialmente a mpochi passi dal ciclo. Diciamo anche che il punto d'incontro è a kpochi passi dall'inizio del ciclo e tartaruga e lepre si incontrano quando la tartaruga ha compiuto ipassi totali. (La lepre avrebbe già compiuto 2ipassi totali da allora.).

Devono essere valide le seguenti 2 condizioni:

1) i = m + p * n + k

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

Il primo dice che la tartaruga muove i ipassi e in questi ipassi arriva prima al ciclo. Quindi passa attraverso i ptempi di ciclo per un numero positivo p. Alla fine supera kpiù nodi fino a quando non incontra la lepre.

Un simile è vero per la lepre. Muove i 2ipassi e in questi 2ipassi arriva prima al ciclo. Quindi passa attraverso i qtempi di ciclo per un numero positivo q. Alla fine supera kpiù nodi fino a quando incontra la tartaruga.

Mentre la lepre viaggia con il doppio della velocità della tartaruga, e il tempo è costante per entrambi quando raggiungono il punto di incontro.

Quindi, usando la semplice relazione velocità, tempo e distanza,

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

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

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

Tra m, n, k, p, q, i primi due sono proprietà dell'elenco indicato. Se possiamo mostrare che esiste almeno un insieme di valori per k, q, p che rende vera questa equazione, mostriamo che l'ipotesi è corretta.

Una di queste soluzioni è la seguente:

p = 0

q = m

k = m n - m

Possiamo verificare che questi valori funzionino come segue:

m + k = ( q - 2p ) n  

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

=> mn = mn.

Per questo set, iè

i = m + p n + k

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

Naturalmente, dovresti vedere che questo non è necessariamente il più piccolo possibile. In altre parole, la tartaruga e la lepre potrebbero essersi già incontrate molte volte. Tuttavia, poiché dimostriamo che si incontrano ad un certo punto almeno una volta, possiamo dire che l'ipotesi è corretta. Quindi dovrebbero incontrarsi se spostiamo uno di loro di 1 passo e l'altro di 2 passi alla volta.

Ora possiamo andare alla seconda parte dell'algoritmo che è come trovare l'inizio del ciclo.

Inizio del ciclo

Una volta che la tartaruga e la lepre si incontrano, rimettiamo la tartaruga all'inizio dell'elenco e manteniamo la lepre dove si sono incontrati (che è k passi dall'inizio del ciclo).

L'ipotesi è che se li lasciamo muovere alla stessa velocità (1 passo per entrambi), la prima volta che si incontreranno di nuovo sarà l'inizio del ciclo.

Dimostriamo questa ipotesi.

Supponiamo per prima cosa che un oracolo ci dica cos'è m.

Quindi, se li lasciamo muovere passi di m + k, la tartaruga dovrebbe arrivare nel punto in cui si sono incontrati originariamente (k passi di distanza dall'inizio del ciclo - vedi in figura).

In precedenza lo avevamo dimostrato m + k = (q - 2p) n.

Poiché m + k passi è un multiplo della lunghezza del ciclo n, la lepre, nel frattempo, attraverserebbe il ciclo (q-2p) volte e ritornerebbe allo stesso punto (k passi all'inizio del ciclo).

Ora, invece di lasciarli muovere m + k passi, se li lasciamo muovere solo m passi, la tartaruga arriverebbe all'inizio del ciclo. La lepre sarebbe k passi prima del completamento delle rotazioni (q-2p). Da quando ha iniziato k passi all'inizio del ciclo, la lepre dovrebbe arrivare all'inizio del ciclo.

Di conseguenza, questo spiega che avrebbero dovuto incontrarsi all'inizio del ciclo dopo un certo numero di passaggi per la prima volta (la prima volta perché la tartaruga era appena arrivata al ciclo dopo m passi e non poteva mai vedere la lepre che era già in il ciclo).

Ora sappiamo che il numero di passaggi necessari per spostarli fino a quando si incontrano risulta essere la distanza tra l'inizio dell'elenco e l'inizio del ciclo, m. Naturalmente, l'algoritmo non ha bisogno di sapere cos'è m. Sposterà sia la tartaruga che la lepre un passo alla volta fino a quando non si incontreranno. Il punto di incontro deve essere l'inizio del ciclo e il numero di passi deve essere la distanza (m) all'inizio del ciclo. Supponendo che conosciamo la lunghezza dell'elenco, possiamo anche calcolare la lunghezza del ciclo di sottrazione di m dalla lunghezza dell'elenco.


1
Non credo sia vero che quando si incontrano questo è il punto di partenza vedi il commento qui sotto: stackoverflow.com/a/19209858/1744146 <br> Per favore fatemi sapere se sbaglio
MrA,

La prima parte della spiegazione è impeccabile. Ma la seconda parte ha un difetto per quanto ne so. Stai assumendo che "qualche oracolo dice m", ma se m è noto, hai già l'inizio del ciclo. Come puoi assumere la risposta quando non sai mai dove si trova l'inizio del ciclo ?? Per favore mi faccia sapere.
Gopichand,

1
@Gopichand Leggi di nuovo l'ultimo paragrafo ... supponi solo che ci sia qualche m (se è già dimostrato che c'è un ciclo) .. ma non conosci il valore di m
Srinath

2
Questa è davvero una spiegazione fantastica. Questa è probabilmente la migliore spiegazione al momento su tutta Internet.
Arlene Batada,

2
La tua equazione m + k = (q - 2p) npuò essere ulteriormente semplificata m + k = q*n. Questo perché il numero di anse della tartaruga sarà sempre zero poiché la lepre non può mai superare la tartaruga senza incontrarla. Pensaci.
Arpit Jain,

124

Fai riferimento a questa immagine:

inserisci qui la descrizione dell'immagine

Distanza percorsa da SlowPointer prima dell'incontro = x + y

Distanza percorsa da FastPointer prima dell'incontro = (x + y + z) + y = x + 2y + z

Poiché fastPointer viaggia con una velocità doppia rispetto a slowPointer e il tempo è costante per entrambi quando si raggiunge il punto di incontro.

Quindi, usando la semplice velocità, tempo e distanza relazione 2 (x + y) = x + 2y + z => x + 2y + z = 2x + 2y => x = z

Quindi spostando slowPointer per avviare l'elenco collegato e facendo in modo che sia slowPointer sia fastPointer per spostare un nodo alla volta, entrambi hanno la stessa distanza da percorrere .

Raggiungeranno il punto in cui inizia il loop nell'elenco collegato.


10
Ciò non tiene conto del caso in cui fastPointer percorre il ciclo n volte prima che slowPointer entri nel ciclo. Usa l per indicare la lunghezza del ciclo. Distanza percorsa da FastPointer prima dell'incontro = (x + y + z) + y = x + 2y + nl + z. E la relazione risultante sarà x = nl + z.
Jingguo Yao,

@JingguoYao: ecco la spiegazione per quel caso.
displayName

2
questo diagramma è troppo semplice. il puntatore veloce può spostarsi più volte nel ciclo prima che il puntatore lento lo raggiunga.
Warren MacEvoy il

70

La risposta semplice e sottovalutata del vecchio monaco spiega come trovare il ciclo quando il corridore veloce completa un solo ciclo completo. In questa risposta spiego il caso in cui il corridore veloce ha eseguito il ciclo più volte prima che il corridore lento entri nel ciclo.


Utilizzando la stessa immagine:inserisci qui la descrizione dell'immagine

Supponiamo che il corridore veloce abbia eseguito il ciclo m volte prima di incontrarsi lentamente e velocemente. Ciò significa che:

  • Distanza percorsa da lento: x + y
  • Distanza percorsa da veloce: x + m (y + z) + y cioè extra y dove si incontrano

Dal momento che la corsa veloce ha una velocità doppia rispetto a quella lenta e che sono state in esecuzione per lo stesso tempo, ciò implica che se raddoppiamo la distanza percorsa da quella lenta, otteniamo la distanza percorsa da quella veloce. Così,

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

Risolvendo per x,

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

Nello scenario reale significherebbe, x = (m - 1) cicli completi + una distanza extra z .

Quindi, se posizioniamo un puntatore all'inizio dell'elenco e lasciamo l'altro nel punto di incontro, spostandoli alla stessa velocità si otterrà il puntatore in loop che completa m - 1 corse del loop e quindi incontra l'altro puntatore a destra all'inizio del ciclo.


7
Un dubbio ... come è garantito che il lento e il veloce si incontreranno prima che il lento impieghi più di un ciclo?
siraj,

4
@siraj: Slow non funzionerà a cicli, come farebbe più veloce che lento ed entrerà nel ciclo prima. Ed è garantito che si incontreranno. Se slow è su j + 1 e fast è su j, ora si incontreranno su j + 2. E se slow è su j e veloce su j + 1, significa che si sono già incontrati su j - 1.
displayName

4
la matematica funziona ancora se slow fa il giro del ciclo: x + (y + z) m + y = 2 (x + (y + z) n + y), dove n è # volte volte attorno al ciclo per lento prima che si incontrino. Questo risolve in (m-2n-1) (y + z) + z = x. Il che significa partire dal punto di incontro, andare in giro (m-2n-1) volte, si torna al punto di incontro, quindi si va z, si è all'inizio del ciclo. E per fare questo è lo stesso che iniziare dal nodo head e andare x nodi.
mayas_mom

1
@mayas_mom: la matematica potrebbe funzionare, ma lentamente non sarà mai in grado di aggirare il ciclo. Sarà sempre catturato o all'inizio o da qualche parte a metà strada.
displayName

4
x = (m - 1) (y + z) + z questo può essere generalizzato poiché la lunghezza del loop è y + z e poiché si preoccupano solo della posizione. Quindi x = ((m - 1) (y + z))% (y + z)) + z Che è effettivamente x = z;
anshul garg,

10

È molto molto semplice Puoi pensare in termini di velocità relativa. Se il coniglio muove due nodi e la tartaruga sposta un nodo, rispetto al coniglio si sposta di un nodo (supponiamo che la tartaruga sia a riposo). Quindi, se spostiamo un nodo nell'elenco circolare collegato, ci incontreremo di nuovo a quel punto.

Dopo aver trovato il punto collegato all'interno dell'elenco dei collegamenti circolari, ora il problema si riduce a trovare il punto di intersezione di due problemi dell'elenco collegato.


8

Figura 1

Al momento della prima collisione, la tartaruga spostava i gradini m + k come mostrato sopra. La lepre si muove due volte più velocemente della tartaruga, il che significa che la lepre si è mossa di 2 (m + k) passi. Da questi semplici fatti possiamo ricavare il seguente grafico.

Figura 1

A questo punto, riportiamo la tartaruga all'inizio e dichiariamo che sia la lepre che la tartaruga devono muoversi di un passo alla volta. Per definizione, dopo m passi, la tartaruga sarà all'inizio del ciclo. Dove sarà la lepre?

La lepre sarà anche all'inizio del ciclo. Questo è chiaro dal secondo grafico: quando la tartaruga è stata spostata indietro all'inizio, la lepre era k passi nel suo ultimo ciclo. Dopo m passi, la lepre avrà completato un altro ciclo e si scontrerà con la tartaruga.


@WarrenMacEvoy In nessun momento ho suggerito di incontrarsi al punto di partenza. Si incontrano di nuovo all'inizio del ciclo, come indicano chiaramente le cifre.
skedastik,

5

Approccio:

Esistono due puntatori:

  • Un puntatore lento che sposta un nodo alla volta.
  • Un puntatore veloce che sposta due nodi alla volta.

Se i due puntatori si incontrano, si prova che esiste un loop. Una volta che si sono incontrati, uno dei nodi punta alla testa e quindi entrambi procedono un nodo alla volta. Si incontreranno all'inizio del ciclo.

Motivazione: Quando due persone percorrono una pista circolare, una alla velocità doppia rispetto all'altra, dove si incontrano? Esattamente da dove hanno iniziato.

Supponiamo ora che il corridore veloce abbia un vantaggio iniziale di kpassaggi in un ngiro di step. dove si incontreranno? Esattamente a n-kpassi. Quando il corridore lento ha coperto i (n-k)passi, il corridore veloce avrebbe coperto i k+2(n-k)passi. (ad es. k+2n-2kpassaggi , ad esempio 2n-kpassaggi ). vale a dire (n-k)passi (il percorso è circolare e non ci preoccupiamo del numero di round dopo i quali si incontrano; ci interessa solo la posizione in cui si incontrano).

Ora, in che modo il corridore veloce ha ottenuto il vantaggio iniziale kin primo luogo? Perché il corridore lento ha impiegato molti passaggi per raggiungere l'inizio del loop. Quindi l'inizio del loop è k passi dal nodo head.

Nota: il nodo in cui entrambi i puntatori si sono incontrati è a kpochi passi dall'inizio del ciclo (all'interno del ciclo) e anche il nodo head è a kpochi passi dall'inizio del ciclo. Quindi quando avremo un puntatore che avanza allo stesso ritmo di 1 passo dal bot di questi nodi, si incontreranno all'inizio del ciclo.

Credo che sia semplice. Per favore fatemi sapere se qualche parte è ambigua.



4

Va bene quindi supponiamo che la lepre e la tartaruga si incontrino in un punto che è k passi di distanza dall'inizio del ciclo, il numero di passi prima dell'inizio del ciclo è mu e la lunghezza del ciclo è L.

Quindi ora al punto d'incontro ->

Distanza coperta da tartaruga = mu + a * L + k - Equazione 1

(Passaggi per raggiungere l'inizio del ciclo + passaggi per coprire le iterazioni 'a' del ciclo + k passaggi dall'inizio del ciclo) (dove a è una costante positiva)

Distanza coperta dalla lepre = mu + b * L + k - Equazione 2

(Passi fatti per raggiungere l'inizio del ciclo + passi fatti per coprire le iterazioni 'b' del ciclo + k passi dall'inizio del ciclo) (dove b è una costante positiva eb> = a)

Quindi la distanza extra coperta dalla lepre è = Equazione 2 - Equazione 1 = (ba) * L

Si noti che questa distanza è uguale alla distanza della tartaruga dal punto di partenza poiché la lepre si muove 2 volte più velocemente della tartaruga. Questo può essere equiparato a 'mu + k' che è anche la distanza del punto d'incontro dall'inizio se non includiamo più attraversamenti del ciclo.

Pertanto, mu + k = (ba) * L

Quindi mu passi da questo punto porterebbero all'inizio del ciclo (poiché sono già stati fatti k passi dall'inizio del ciclo per raggiungere il punto di incontro). Questo potrebbe accadere nello stesso ciclo o in uno qualsiasi dei cicli successivi. Quindi ora se spostiamo la tartaruga all'inizio dell'elenco collegato, ci vorranno passi mu per raggiungere il punto iniziale del ciclo e la lepre farebbe passi mu per raggiungere anche l'inizio del ciclo e quindi si incontrerebbero entrambi al punto di partenza del ciclo.

PS Onestamente, avevo in mente la stessa domanda del poster originale e ho letto la prima risposta, hanno chiarito alcune cose ma non sono riuscito a ottenere chiaramente il risultato finale e quindi ho cercato di farlo a modo mio e trovato più facile da capire.


di solito non si incontrano all'inizio del ciclo
Warren MacEvoy il

3

inserisci qui la descrizione dell'immagine credito di immagine

Chiama la distanza il numero di collegamenti che un puntatore segue e il tempo il numero di iterazioni che l'algoritmo impiega per spostare il puntatore lento di un collegamento e il puntatore veloce di due collegamenti. Ci sono N nodi prima di un ciclo di lunghezza C, etichettati con offset del ciclo da k = 0 a C-1.

Per raggiungere l'inizio del ciclo, il lento richiede N tempo e distanza. Questo significa che velocemente prende N distanza nel ciclo (N per arrivarci, N per girare). Quindi al tempo N, lento è all'offset del ciclo k = 0 e veloce è all'offset del ciclo k = N mod C.

Se N mod C è zero, lento e veloce ora corrispondono e il ciclo si trova al momento N e posizione del ciclo k = 0.

Se N mod C non è zero, allora veloce ora deve recuperare con lento, che al momento N è C- (N mod C) distanza indietro nel ciclo.

Poiché le mosse veloci 2 per ogni 1 di lento, riducendo la distanza di 1 per ogni iterazione, ciò richiede tanto tempo aggiuntivo quanto la distanza tra veloce e lento al tempo N, che è C- (N mod C). Poiché il movimento lento si sposta dall'offset 0, questo è anche l'offset in cui si incontrano.

Quindi, se N mod C è zero, la fase 1 si interrompe dopo N iterazioni all'inizio del ciclo. Altrimenti, la fase 1 si interrompe dopo iterazioni N + C- (N mod C) all'offset C- (N mod C) nel ciclo.

// 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;
}

Ok, quindi fase 2: lento fa N più passi per arrivare al ciclo, a quel punto veloce (ora muovendo 1 per passo temporale) è a (C- (N mod C) + N) mod C = 0. Quindi si incontrano all'inizio del ciclo dopo la fase 2.

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

Per completezza, la fase 3 calcola la durata del ciclo spostandosi ancora una volta nel ciclo:

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

Link al documento di Google per simulare l'algoritmo: docs.google.com/spreadsheets/d/…
Warren MacEvoy

1
Si noti che, se N <= C, l'iterazione si interrompe dopo le iterazioni C. In ogni caso, deve arrestarsi in fasi inferiori a N + C ed è improbabile che si fermi all'inizio del ciclo.
Warren MacEvoy,

2

Ridurre il problema a un problema ad anello, quindi tornare al problema iniziale

Trovo la seguente spiegazione più intuitiva.

  1. Prendi due puntatori ( 1 = tartaruga e 2 = lepre) che iniziano dalla testa ( O ), 1 ha una lunghezza del passo di 1 , 2 ha una lunghezza del passo di 2 . Pensa al momento in cui 1 raggiunge il nodo iniziale di quel ciclo ( A ).

    Vogliamo rispondere alla seguente domanda "Dov'è 2 quando 1 è in A?" .

    Quindi, OA = aè un numero naturale ( a >= 0). Ma può essere scritto nel modo seguente:, a = k*n + bdove a, k, n, b are natural numbers:

    • n = la durata del ciclo
    • k >= 0 = costante
    • 0 <= b <= n-1

    Significa che b = a % n.

    Ad esempio: if a = 20e n = 8=> k = 2e b = 4because 20 = 2*8 + 4.

    La distanza coperta da 1 è d = OA = a = k*n + b. Ma allo stesso tempo, 2 copertine D = 2*d = d + d = OA + d = OA + k*n + b. Ciò significa che quando 2 è in A deve coprire k*n + b. Come puoi vedere, kè il numero di giri, ma dopo questi giri, 2 sarà b lontano da A. Quindi, abbiamo trovato dove si trova 2 quando 1 è in A. Chiamiamo quel punto B, dove AB = b.

    inserisci qui la descrizione dell'immagine

  2. Ora riduciamo il problema a un cerchio. La domanda è "Dov'è il punto d'incontro?" . Dov'è quella C ?

    inserisci qui la descrizione dell'immagine

    In ogni passaggio, 2 riduce la distanza da 1 con 1(diciamo metro) perché 1 si allontana da 2 con 1, ma allo stesso tempo 2 si avvicina a 1 di 2.

    Quindi, l'intersezione sarà quando la distanza tra 1 e 2 sarà zero. Ciò significa che 2 riduce la n - bdistanza. Per raggiungere questo obiettivo, 1 farà n - bpassi, mentre 2 farà 2*(n - b)passi.

    Quindi, il punto di intersezione sarà n - blontano da A (in senso orario), poiché questa è la distanza percorsa da 1 fino a quando non incontra 2 . => la distanza tra C e A è CA = b, perché AC = AB + BC = n - be CA = n - AC. Non pensare che AC = CA, poiché la ACdistanza non è una banale distanza matematica, è il numero di passi tra A e C (dove A è il punto iniziale e C è il punto finale).

  3. Ora torniamo allo schema iniziale.

    Lo sappiamo a = k*n + be CA = b.

    Possiamo prendere 2 nuovi puntatori 1 ' e 1' ' , dove 1' inizia dalla testa ( O ) e 1 '' inizia dal punto di intersezione ( C ).

    Mentre 1 ' passa da O ad A , 1' ' passa da C ad A e continua a finire i kgiri. Quindi, il punto di intersezione è A .

    inserisci qui la descrizione dell'immagine

    inserisci qui la descrizione dell'immagine


2

inserisci qui la descrizione dell'immagine

Se i puntatori si incontrano in un punto P come mostrato in figura, la distanza Z + Y è il punto P e X + Y è anche il punto P che significa Z = X. Ecco perché mantenere spostando un puntatore da P e spostarne un altro dall'inizio (S) fino a quando si incontrano, il che significa spostare una uguale distanza (Z o X) sullo stesso punto M (distanza Z da P e X da S) sarà il inizio del ciclo. Semplice!


1

Con tutte le analisi di cui sopra, se sei una persona di esempio, ho provato a scrivere una breve analisi e un esempio che aiutano a spiegare la matematica che tutti gli altri hanno tentato di spiegare. Eccoci qui!

Analisi:

Se abbiamo due puntatori, uno più veloce dell'altro, e li spostiamo insieme, alla fine si incontreranno di nuovo per indicare un ciclo o null per indicare nessun ciclo.

Per trovare il punto di partenza del ciclo, lascia ...

  1. m essere la distanza dalla testa all'inizio del ciclo;

  2. d essere il numero di nodi nel ciclo;

  3. p1 essere la velocità del puntatore più lento;

  4. p2essere la velocità del puntatore più veloce, ad es. 2 significa passare attraverso due nodi alla volta.

    Rispettare le seguenti iterazioni:

 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

Dai dati di esempio sopra riportati, possiamo facilmente scoprire che ogni volta che i puntatori più veloci e più lenti si incontrano, sono a mpochi passi dall'inizio del ciclo. Per risolvere questo problema, rimetti il ​​puntatore più veloce in testa e imposta la sua velocità sulla velocità del puntatore più lento. Quando si incontrano di nuovo, il nodo è l'inizio del ciclo.


1

diciamo,

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

abbiamo 2 puntatori A e B, A funziona a velocità 1x, B a velocità 2x, entrambi iniziano all'inizio.

quando A raggiunge N [0], B dovrebbe essere già in N [m]. (Nota: A usa m passi per raggiungere N [0] e B dovrebbe essere m passi ulteriormente)

Quindi, A esegue k più passaggi per scontrarsi con B, ovvero A è a N [k], B è a N [m + 2k] (Nota: B dovrebbe essere eseguito per 2k passi a partire da N [m])

A si scontrano B in N [k] e N [m + 2k] rispettivamente, significa k = m + 2k, quindi k = -m

Pertanto, per tornare indietro a N [0] da N [k], abbiamo bisogno di m più passi.

Detto semplicemente, abbiamo solo bisogno di eseguire altri m dopo aver trovato il nodo di collisione. Possiamo avere un puntatore da eseguire dall'inizio e un puntatore in esecuzione dal nodo di collisione, che si incontreranno in N [0] dopo m passi.

Pertanto, lo pseudo codice è il seguente:

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

Non penso che sia vero che quando si incontrano questo è il punto di partenza. Ma sì, se l'altro puntatore (F) era nel punto di incontro prima, allora quel puntatore si troverà alla fine del ciclo anziché all'inizio del ciclo e il puntatore (S) che è iniziato dall'inizio dell'elenco sarà finisce all'inizio del ciclo. per esempio:

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

Una semplice spiegazione usando l'idea di velocità relativa insegnata al liceo - lezioni di Fisica 101 / cinematica.

Cerchio in LinkedList

  1. Supponiamo che la distanza dall'inizio dell'elenco collegato all'inizio del cerchio sia xsalti. Chiamiamo l'inizio del cerchio come punto X(in maiuscolo - vedi figura sopra). Supponiamo anche che la dimensione totale del cerchio sia N hop.

  2. Velocità della lepre = 2 * Velocità della tartaruga. Quindi questo è 1 hops/sece 2 hops/secrispettivamente

  3. Quando la tartaruga raggiunge l'inizio del cerchio X, la lepre deve essere ulteriormente xsaltata nel punto Yin figura. (Perché la lepre ha percorso il doppio della distanza rispetto alla tartaruga).

  4. Pertanto, la lunghezza dell'arco rimanente in senso orario da X a Y sarebbe N-x. Questa è anche la distanza relativa da percorrere tra la lepre e la tartaruga affinché possano incontrarsi . Diciamo che questa distanza relativa sarà coperta nel tempo, t_mcioè nel tempo per incontrarci. La velocità relativa è (2 hops/sec - 1 hops/sec)cioè 1 hops/sec. Quindi, usando, distanza relativa = velocità relativa X tempo, otteniamo, t= N-xsec. Quindi ci vorrà N-xper raggiungere il punto d'incontro sia per la tartaruga che per la lepre.

  5. Ora al secondo N-xtempo e alla 1 hops/secvelocità, la tartaruga che era in precedenza al punto Xcoprirà i luppoli di Nx per raggiungere il punto di incontro M. Quindi, ciò significa che il punto di incontro Mè in corrispondenza dei punti di partenza in N-xsenso antiorario da X= (il che implica ulteriormente) => che rimane una xdistanza rimanente dal punto Min Xsenso orario.

  6. Ma xè anche la distanza per raggiungere il punto Xdall'inizio dell'elenco collegato.

  7. Ora, non ci interessa a quale numero di luppoli xcorrisponde. Se mettiamo una tartaruga all'inizio della LinkedList e una tartaruga nel punto di incontro Me lasciamo che saltino / camminino, allora si incontreranno nel punto X, che è il punto (o nodo) di cui abbiamo bisogno.


1

Lavorare con un diagramma sarebbe di aiuto. Sto cercando di spiegare il problema senza equazioni.

  1. Se lasciamo correre la lepre e la tartaruga in un cerchio e la lepre corre due volte la tartaruga, alla fine di un giro la tartaruga sarebbe a metà. Alla fine di due giri dalla tartaruga lepre avrebbe fatto 1 giro ed entrambi si incontrano. Questo vale per tutte le velocità come se la lepre corre tre volte, la lepre 1 giro è uguale a 1/3 della tartaruga, quindi alla fine di 3 giri per la lepre la tartaruga avrebbe coperto 1 giro e si incontrano.
  2. Ora se li avviamo m passi prima del loop, allora significa che la lepre più veloce sta iniziando avanti nel loop. Quindi se la tartaruga raggiunge l'inizio del ciclo, la lepre è m passi avanti del ciclo e quando si incontrano sarebbe m passi prima dell'inizio del ciclo.

1

-Ci sono k passi prima del ciclo. Non sappiamo cos'è k e non è necessario scoprirlo. Possiamo lavorare astrattamente con solo k.

--Dopo k passaggi

----- T è all'inizio del ciclo

----- H è k passi nel ciclo (è andato 2k in totale e quindi k nel ciclo)

** ora sono loopsize - a parte k

(nota che k == K == mod (loopsize, k) --eg se un nodo ha 2 passi in un ciclo di 5 nodi, ci sono anche 7, 12 o 392 passi, quindi quanto grande è il ciclo k k non lo fa fattore in.

Dal momento che si raggiungono al ritmo di 1 passo per unità di tempo perché uno si sta muovendo due volte più velocemente dell'altro, si incontreranno a loopsize - k.

Ciò significa che ci vorranno k nodi per raggiungere l'inizio del ciclo e quindi la distanza dalla testa al ciclestart e la collisione al ciclestart sono gli stessi.

Quindi, dopo la prima collisione, sposta T di nuovo alla testa. T e H si incontreranno al ciclestart se ti muovi al ritmo di 1 ciascuno. (in k passaggi per entrambi)

Ciò significa che l'algoritmo è:

  • dalla testa muovi T = t.next e H.next.next fino a quando si scontrano (T == H) (c'è un ciclo)

// prendi cura del caso quando k = 0 o T e H si incontrano all'inizio del loop calcolando la lunghezza del loop

- Conta la lunghezza del ciclo spostando T o H attorno ad esso con un contatore

- sposta un puntatore T2 in cima alla lista

--move la lunghezza del puntatore delle fasi del ciclo

--muovi un altro puntatore H2 alla testa

- spostare T2 e H2 in tandem fino a quando si incontrano all'inizio del ciclo

questo è tutto!


1

Ci sono già molte risposte a questo, ma una volta ho escogitato un diagramma per questo che è visivamente più intuitivo per me. Forse può aiutare altre persone.

I principali momenti aha per me sono stati:

  • Dividere T (tartaruga) in T1 (pre-loop) e T2 (in-loop). T = tartaruga, H = lepre

  • Sottrai T da H , dove si sovrappongono visivamente. Ciò che rimane ( H - T = H' ) è pari a T .

  • La matematica rimanente è abbastanza semplice. Da H, sottrarre dove T si sovrappone visivamente

-1

So che esiste già una risposta accettata per questo problema, ma cercherò comunque di rispondere in modo fluido. Supponiamo:

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.

Ora, lascia che la lepre e la tartaruga si incontrino dopo il tempo 't' dall'inizio.

osservazioni:

Se, Distanza percorsa dalla tartaruga = v * t = x + (bk) (diciamo)

Quindi, Distanza percorsa dalla lepre = 2 * v * t = x + (b - k) + b (poiché la lepre ha già attraversato la parte ad anello una volta)

Ora, gli orari delle riunioni sono gli stessi.

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

=> x = k

Questo ovviamente significa che la lunghezza del percorso che non è in loop è uguale alla distanza del punto iniziale del loop dal punto in cui entrambi si incontrano.


Non puoi presumere che la tartaruga abbia viaggiato esattamente x + bk quando si incontrano. Inoltre, non capisco come hai ottenuto x + 2 * bk per la distanza della lepre.
Plumenatore,

Perché la lepre avrebbe attraversato la parte ad anello una volta già per incontrare la tartaruga .. Non l'ho spiegato lì: /
n0nChun

-1

In realtà è facile dimostrare che entrambi si incontreranno al punto di partenza, se si considera la matematica dietro il punto di incontro.
Innanzitutto, m indica il punto iniziale del ciclo nell'elenco collegato e n indica la lunghezza del ciclo. Quindi, affinché la lepre e la tartaruga si incontrino, abbiamo:

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

Dichiarandolo più matematicamente:

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

quindi si incontreranno al momento t che dovrebbe essere un multiplo della durata del ciclo. Ciò significa che si incontrano in un luogo, che è (t-m) modulo n = (0-m) modulo n = (-m) modulo n.

Quindi ora tornando alla domanda, se sposti un puntatore dall'inizio dell'elenco collegato e un altro dal punto di intersezione, dopo m passi avremo la lepre (che si sta muovendo all'interno del ciclo) arrivando a un punto che è ((-m) + m) modulo n = 0 modulo nche non è altro che il punto di partenza del ciclo. Quindi possiamo vedere che dopo m passi arriva all'inizio del ciclo e la tartaruga lo incontrerà lì mentre attraverserà m passi dall'inizio della lista collegata.

Come nota a margine, possiamo anche calcolare il tempo della loro intersezione in questo modo: la condizione t = 0 modulo nci dice che si incontreranno in un momento che è un multiplo della lunghezza del ciclo, e inoltre t dovrebbe essere maggiore di m come si incontrerebbero in il ciclo . Quindi il tempo impiegato sarà uguale al primo multiplo di n che è maggiore di m .


Non si incontrano necessariamente all'inizio del ciclo.
Warren MacEvoy il

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.