2010-02-07 13 views
25

Supponiamo che ci siano due elenchi concatenati singoli che si intersecano in un punto e diventano una singola lista collegata.Individuazione del nodo intersecante da due elenchi collegati intersecanti

I puntatori di testa o di inizio di entrambi gli elenchi sono noti, ma il nodo intersecante non è noto. Inoltre, il numero di nodi in ciascuna lista prima di intersecare è sconosciuto e entrambi gli elenchi possono averlo diverso, ad esempio List1 potrebbe avere n nodi prima che raggiunga il punto di intersezione e List2 potrebbe avere m nodi prima che raggiunga il punto di intersezione dove m e n potrebbero essere

  • m = n,
  • m < n o
  • m> n

Un noto o semplice soluzione è quella di confrontare ogni puntatore nodo nel primo elenco con ogni altro puntatore nodo nella seconda lista da cui il nodo corrispondente poi le reti ci porteranno al nodo intersecante. Ma la complessità temporale in questo caso sarà O (n) che sarà alta.

Qual è il modo più efficace per trovare il nodo intersecante?

+0

Vedi anche [Codice di produzione per trovare junction in una lista collegata] (http://stackoverflow.com/questions/22307 18/produzione-code-per-finding-junction-in-a-linked-list). Non è un duplicato, ma sta estendendo il problema che viene posto qui. –

risposta

46

Questo richiede O (M + N) tempo e O (1) spazio, dove M e N sono la lunghezza totale degli elenchi collegati. Forse inefficiente se la parte comune è molto lungo (cioè M, N >> m, n)

  1. Traverse i due lista collegata per trovare M e N.
  2. tornare alle teste, poi traverse | M − N | nodi nella lista più lunga.
  3. Ora cammina nella fase di blocco e confronta i nodi finché non trovi quelli comuni.

Edit: Vedere http://richardhartersworld.com/cri/2008/linkedlist.html

+0

Mi piace! +1. –

+0

Accettando questa risposta in quanto non è necessario modificare l'elenco e inoltre non mangia spazio extra. Ma, mi sto ancora chiedendo se non ci sono soluzioni migliori di questo. Comunque, grazie mille per la tua risposta e anche per gli altri. – Jay

+0

La risposta di @Jakob Borg non incorre in un ciclo di iterazione in meno che calcoli la differenza di lunghezza delle due liste concatenate? – user1071840

16

Se possibile, si potrebbe aggiungere un campo di 'colore' o simili ai nodi. Scorri una delle liste, colorando i nodi mentre vai. Quindi scorrere il secondo elenco. Non appena raggiungi un nodo che è già colorato, hai trovato l'intersezione.

+1

Questo è possibile solo quando è consentita la modifica della lista. – 0xc0de

7

Eseguire il dump del contenuto (o dell'indirizzo) di entrambi gli elenchi in una tabella hash. la prima collisione è la tua intersezione.

+0

Se ci sono 2 liste collegate come 1-2-3-4-3-5 e 9-8-3-5. E il 2 ° elenco collegato interseca il primo al secondo 3 °. Ma se usiamo un po 'di tabella hash, questo si scontrerà alla prima posizione di 3. –

+0

un elenco collegato non può contenere 3 due volte a meno che non sia già contorto lo in un "6" invece di un elenco di linerari. Le liste collegate – ddyer

+0

possono avere valori ripetuti, quindi il ripetuto 3 è molto legale. La collisione degli indirizzi è la maniera corretta. – 0xc0de

1

Controllare gli ultimi nodi di ogni elenco. Se c'è un'intersezione, il loro ultimo nodo sarà lo stesso.

+3

La domanda ha lo scopo di trovare il nodo intersecante. – JavaDeveloper

0

Questa è la soluzione pazza che ho trovato durante la codifica a tarda notte, si 2x più lento di risposta accettata, ma utilizza un bel trucco aritmetica:

public ListNode findIntersection(ListNode a, ListNode b) { 
    if (a == null || b == null) 
     return null; 

    int A = a.count(); 
    int B = b.count(); 

    ListNode reversedB = b.reverse(); 

    // L = a elements + 1 c element + b elements 
    int L = a.count(); 

    // restore b 
    reversedB.reverse(); 

    // A = a + c 
    // B = b + c 
    // L = a + b + 1 

    int cIndex = ((A+B) - (L-1))/2; 
    return a.atIndex(A - cIndex); 
} 

Abbiamo diviso liste in tre parti: a questo fa parte del primo elenco fino all'inizio della parte comune, b questo è parte del secondo elenco fino alla parte comune e c che è la parte comune di due elenchi. Contiamo le dimensioni delle liste e quindi la lista inversa b, questo causerà che quando avvieremo la lista di movimento da a fine termineremo allo reversedB (andremo a a -> firstElementOfC -> reversedB). Questo ci darà tre equazioni che ci permettono di ottenere la lunghezza della parte comune c.

Questo è troppo lento per la programmazione di competizioni o l'uso in produzione, ma penso che questo approccio sia interessante.

0

Forse irrilevante a questo punto, ma ecco il mio sporco approccio ricorsivo. Questo richiede tempo e O(M)O(M) spazio, dove M >= N per list_M di lunghezza M e list_N di lunghezza N

  1. ricorsivamente iterare al fine di entrambi gli elenchi, quindi contati dalla fine del passo 2. Si noti che list_N colpirà null prima list_M, per M > N
  2. stesse lunghezze M=N interseca quando list_M != list_N && list_M.next == list_N.next
  3. diverse lunghezze M>N interseca quando list_N != null

Esempio di codice:

Node yListsHelper(Node n1, Node n2, Node result) { 
    if (n1 == null && n2 == null) 
     return null; 
    yLists(n1 == null ? n1 : n1.next, n2 == null ? n2 : n2.next, result); 
    if (n1 != null && n2 != null) { 
     if (n2.next == null) { // n1 > n2 
      result.next = n1; 
     } else if (n1.next == null) { // n1 < n2 
      result.next = n2; 
     } else if (n1 != n2 && n1.next == n2.next) { // n1 = n2 
      result.next = n1.next; // or n2.next 
     } 
    } 
    return result.next; 
} 
Problemi correlati