Trovare l'altezza di tutti i nodi in una foresta


8

Ho una foresta, cioè nodi con bordi diretti e nessun ciclo (diretto o non diretto). Definisco l'altezza di un vertice come 0 se non ha spigoli in arrivo o il numero massimo di spigoli da attraversare all'indietro per raggiungere un vertice di altezza 0. vinserisci qui la descrizione dell'immagine

So anche che il grado medio di un nodo è una piccola costante, diciamo 2 o giù di lì. Per trovare l'altezza di tutti i vertici, posso pensare a due algoritmi:

Algoritmo ambulante

  1. Passare attraverso e contrassegnare per i vertici senza bordi in entrata.h=0
  2. Per ogni vertice con , seguire i bordi in uscita, aggiornando l'altezza di ciascun vertice rilevato se la sua altezza precedente è inferiore.h=0

Algoritmo di frontiera

  1. Passare attraverso e contrassegnare per i vertici senza bordi in arrivo e contrassegnarli come frontiera.h=0
  2. Per ogni vertice di frontiera, vedi se il suo genitore ha figli alla frontiera o al di sotto di essa, in tal caso, segna il genitore come con l'altezza maggiore tra i suoi figli. Segna il genitore come al confine.1
  3. Ripeti 2 fino a quando non c'è nulla oltre la frontiera.

Le mie domande:

  1. Esiste un nome per questo problema e una soluzione ben nota e veloce?
  2. Tendo a pensare semplicemente salendo da tutto il h=0i vertici è la soluzione più veloce. Ho ragione?

Risposte:


7

Prima di tutto, dipende un po 'da come puoi accedere ai tuoi dati per dire quali algoritmi funzionano meglio.

Ad ogni modo, suggerirei di determinare le altezze in modo discendente anziché discendente. Personalmente penso che un approccio dall'alto verso il basso sia concettualmente più bello e più facile da analizzare. Per ogni verticev nella foresta è vero che

altezza(v)={(maxu figlio di valtezza(u))+1Se u non è una foglia0altrimenti.

Quindi puoi cercare tutte le radici e quindi determinare le altezze dividendo una conquista. Toccerai ogni vertice al massimo due volte (scansionando le radici + attraversando). Nell'approccio che hai suggerito potresti dover toccare alcuni vertici molte volte.

A proposito, poiché hai una foresta, hai meno spigoli che vertici, quindi sai che hai un grado medio inferiore a due (e quindi puoi testare le radici in tempo lineare).


+1; le soluzioni ricorsive sono spesso più facili da analizzare. Dipende anche dal fatto che tu abbia già dei puntatori figlio o meno e che tu voglia una soluzione basata su loop o su ricorsione.
Joe,

Mi piace l'analisi! Qualcuno può aiutare i neofiti sottolineando come convertire anche questo in forma iterativa?
highBandWidth,

4

Non so se questo problema abbia o meno un nome ufficiale. Il tuo titolo lo riassume abbastanza bene. Salire da nodi con altezza 0 sarà veloce, a condizione che si eviti di duplicare il lavoro. Supponiamo di avere un nodo con molti figli e un lungo percorso sopra questo nodo alla radice. Supponiamo anche che le altezze di ciascuno dei bambini siano diverse. Ogni bambino può aggiornare l'altezza del nodo in questione. Va bene. Ma dovresti evitare di aggiornare anche il lungo percorso sopra quel nodo fino a quando tutti i suoi figli non hanno segnalato le loro altezze.

L'algoritmo risultante verrà eseguito in tempo lineare e lo pseudo-codice sarebbe simile a questo:

initialize a queue Q
initialize all nodes to have a property: maxChildHeight = 0
initialize all nodes of in-degree 0 to have height = 0
Add all nodes of in-degree 0 to Q
while Q is non-empty:
  pop a node v from the front of Q
  subtract 1 from the indegree of the parent of v
  set parent.maxChildHeight = max(height(v), parent.maxChildHeight)
  if the indegree of the parent is 0:
      parent.height =  maxChildHeight + 1
      add the parent to Q

3

Un problema così simile che potrebbe interessare è "Prefisso parallelo su alberi con orientamento radicato". L'algoritmo trova il numero di spigoli nella radice da ciascun nodo. Quindi le radici finiscono con un valore 0, mentre ad esempio il nodo in basso a destra finirebbe con un valore di due.

Si noti che l'algoritmo seguente risolve il problema più generale per i bordi ponderati, ma è possibile inizializzare W (i) su 1 per tutti i. E ogni nodo i successore è dato da P (i) = j.

for 1 ≤ i ≤ n do in parallel
    S(i) = P(i)
    while S(i) != S(S(i)) do
        W(i) = W(i) + W(S(i))
        S(i) = S(S(i))

L'immagine seguente illustra la "metà" delle lunghezze del percorso e rende il tempo di esecuzione logaritmico di facile comprensione. Tuttavia, non mostra le altezze dei nodi calcolate.

inserisci qui la descrizione dell'immagine

(da "Introduzione agli algoritmi paralleli" di Joseph Jaja).

Utilizzando più processori, è risolvibile in tempo O (lg n), ma utilizza operazioni O (n lg n). C'è un trucco per farlo scendere al lavoro lineare, ma è leggermente coinvolto.


Grazie! cosa S(i)rappresenta?
highBandWidth,

Il nodo successore. Quindi nell'iterazione uno dell'albero giusto, S (9) = 10, S (10) = 11, S (11) = 12, S (12) = 13 e W (9) = 1, W (10) = 1 , W (11) = 1, W (12) = 1. Nell'iterazione due, S (9) = 11, S (10) = 12, S (11) = 13, S (12) = 13 e W (9) = 2, W (10) = 2, W (11) = 2, W (12) = 1. Nell'iterazione tre, S (9) = 13, S (10) = 13, S (11) = 13, S (12) = 13 e W (9) = 2 + 2, W (10) = 2 + 1, W (11) = 2, W (12) = 1.
The Unfun Cat,

Devi immaginare che tutte le S (i) e W (i) vengano aggiornate contemporaneamente quando provi a elaborare i dettagli. Questo potrebbe essere oscuro, ma volevo pubblicarlo perché è un classico problema parallelo e molto vicino a quello che hai descritto.
The Unfun Cat,
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.