martedì 28 agosto 2012

Algoritmo della settimana: Trovare il più basso antenato comune


Introduzione

Ecco un compito relativo alla struttura di dati ad albero. Dati due nodi, si può trovare il loro minimo comune antenato?
È un dato di fatto, questo compito ha sempre una soluzione adeguata perché almeno il nodo radice è sempre un antenato comune di tutte le coppie di nodi. Tuttavia, qui il compito è di trovare il più basso, che può essere abbastanza lontano dalla radice.
Trovare il più basso antenato comune

A noi non importa che tipo di alberi che abbiamo. Tuttavia la soluzione, come vedremo, può essere molto diverso a seconda del tipo di albero. In effetti trovare il minimo comune antenato può avere complessità lineare per gli alberi binari di ricerca, che non è vero per gli alberi normali.

Panoramica

Diciamo che abbiamo un albero (non binario!) E due nodi di questo albero. Il compito è quello di trovare il loro minimo comune antenato. Il fatto è che non ne so molto di cui sembrano essere nella struttura.
Possiamo pensare a questo albero come un albero DOM di una linea singola pagina HTML. Non è binario o bilanciata e non può essere sicuro dove questi nodi sono.
Più basso antenato comune

Prima dobbiamo trovare entrambi i percorsi dalla radice a ciascuno dei nodi di destinazione. Si noti che questo richiede memoria aggiuntiva! Poi, nel tempo lineare, siamo in grado di passare attraverso questi "percorsi" ed eseguirà la scansione dalla radice verso il basso per i nodi. Ci aspettiamo che questi array di essere uguali almeno nel loro primo elemento (la radice). L'utilizzo di questo scenario il più basso antenato comune è l'ultimo elemento uguale in entrambi gli array.
Percorsi più basso antenato comune
Una volta che sappiamo i percorsi dalla radice verso il basso per i nodi, possiamo confrontare, al fine di trovare il minimo comune antenato!
Per vedere come questo algoritmo può essere cambiato radicalmente a seconda della struttura dei dati, vediamo un altro esempio. Ora diciamo che abbiamo un albero binario di ricerca (BST). Sappiamo che in un BST tutti gli elementi della sub-tree sinistra sono più piccole della radice e tutti gli elementi sulla destra sub-tree sono maggiori della radice. Questo è vero anche per la sinistra e la destra sotto-alberi.
Ora, perché stiamo cercando il più basso antenato comune, non abbiamo bisogno di raccogliere i percorsi dalla radice ai nodi in due matrici. Sappiamo solo che gli elementi superiori siano a destra, mentre gli oggetti più piccoli sono a sinistra. Questo può aiutarci a trovare la tariffa più antenato partendo direttamente dalla radice.
Minimo comune in un BST
In un BST confrontiamo entrambi i valori con un dato nodo (a partire dalla radice). Nel caso in cui il valore del nodo è in mezzo a loro - questo è il minimo antenato comune. In caso contrario - andiamo sia a sinistra oa destra!
Quello che facciamo è quello di confrontare le due chiavi dei nodi di destinazione con il tasto principale.Se uno dei tasti sono più piccoli, e l'altro è maggiore di chiave della radice, allora ovviamente la radice è il più basso antenato comune. Questo è vero perché uno degli elementi sarà in qualche parte sinistra sub-tree, mentre l'altro sarà nella giusta sub-tree.
Nel caso in cui entrambi i valori sono maggiori (o minori) che la radice, siamo in grado di spostarsi a destra (oa sinistra) sub-tree e riprovare con la stessa procedura. Così il primo nodo che chiave è tra i due valori di destinazione sarà il più basso antenato comune.

Codice

Ecco una applicazione molto semplice PHP che ci mostra questi due algoritmi.

01.classe Albero
02.{
03.pubblico $ nodo = null;
04.pubblico $ id = null;
05.pubblico $ genitore = null;
06.pubblico $ bambini matrice ();
07. 
08.pubblica funzione __construct ( $ node $ id = null)
09.{
10.$ this -> nodo = $ nodo ;
11.$ this -> id = $ id ;
12.}
13. 
14.pubblica funzione addChild (Node e $ n )
15.{
16.$ n - genitore> = $ this ;
17.$ this -> figli [] = $ n ;
18.}
19. 
20./ **
21.* Restituisce un elemento dal suo ID
22.*
23.* @ param $ id mescolati
24.* @ return nodo
25.* /
26.pubblica funzione di ricerca ( $ id )
27.{
28.se $ this -> id == $ id ) {
29.ritorno $ this ;
30.}
31. 
32.$ a = false;
33. 
34./ / ricerca in tutti i bambini a partire dai più a sinistra
35.foreach $ this -> bambini da $ bambino ) {
36.$ a $ bambino -> Ricerca ( $ id );
37.}
38. 
39.ritorno $ a ;
40.}
41. 
42./ **
43.* Trova un percorso dalla radice alla
44.* voce e la restituisce come una lista
45.*
46.​​* @ param $ id mescolati
47.* @ return array di
48.* /
49.pubblica funzione find_path ( $ id , & $ path )
50.{
51.array_push $ path $ this -> id);
52. 
53.se $ this -> id == $ id ) {
54.ritorno 1;
55.}
56. 
57.foreach $ this -> bambini da $ bambino ) {
58.se (1 == $ bambino -> find_path ( $ id $ path )) di ritorno 1;
59.array_pop $ path );
60.}
61.}
62. 
63.pubblica funzione __toString ()
64.{
65.ritorno $ this - nodo>. $ this -> id. "\ n" ;
66.}
67.}
68. 
69.$ dom nuovo Albero ( 'DOM' 'root' );
70. 
71.$ corpo nuovo Albero ( 'BODY' , 1);
72.$ div1 nuovo Albero ( 'DIV' 'div-1' );
73.$ div2 nuovo Albero ( 'DIV' 'my-id' );
74. 
75.$ a nuovo albero ( "A" 'un po' di-link ' );
76. 
77.$ dom - addChild> ( $ body );
78.$ corpo - addChild> ( $ div1 );
79.$ corpo - addChild> ( $ div2 );
80.$ div2 - addChild> ( $ a );
81. 
82.$ path1 $ path2 matrice ();
83.$ dom -> find_path ( 'div-1' $ path1 );
84.$ dom -> find_path ( 'un po-link' $ path2 );

Trovare più basso antenato comune in un BST


01.classe Albero
02.{
03.pubblica $ chiave ;
04. 
05.pubblico $ genitore   = null;
. 06pubblica $ sinistra     = null;
07.pubblico $ destra    = null;
08. 
09.pubblica funzione __construct ( $ key )
10.{
11.$ this -> key = $ chiave ;
12.}
13. 
14.pubblica funzione insert (Albero $ n )
15.{
16.se $ this -> tasto < $ n - tasto>) {
17.se $ this -> destro == null) {
18./ / inserire
19.$ this -> diritto = $ n ;
. 20$ n - genitore> = $ this ;
21.else {
22.$ this -> tasto destro> insert ( $ n );
23.}
24.}
25.se $ this -> tasto> $ n - tasto>) {
26.se $ this -> sinistro == null) {
27./ / inserire
28.$ this -> sinistra = $ n ;
29.$ n - genitore> = $ this ;
30.else {
31.$ this -> sinistra-> insert ( $ n );
32.}
33.}
34.}
35.}
36. 
37.$ t nuovo albero (10);
38. 
39.$ n1 nuovo albero (20);
. 40$ n2 nuovo albero (5);
41.$ n3 nuovo albero (7);
42.$ n4 nuovo albero (13);
43. 
44.$ t - inserto> ( $ n1 );
. 45$ t - inserto> ( $ n2 );
46.​​$ t -> insert ( $ n3 );
47. 
48.funzione find_common ( $ node1 node2 $ $ albero )
49.{
50.se $ node1 -> tasto < $ albero -> && chiave $ node2 -> tasto> $ albero- tasto>) {
51.ritorno $ albero ;
52.else se $ node1 -> tasto < $ albero -> && chiave $ node2 -> tasto <$ albero - tasto>) {
53.find_common ( $ node1 node2 $ $ albero - a sinistra>);
54.else se $ node1 -> tasto> $ albero -> && chiave $ node2 -> tasto> $ albero - tasto>) {
55.find_common ( $ node1 node2 $ $ albero -> destra);
56.}
57.}
58. 
59.$ nodo = find_common ( $ n3 n4 $ $ t );

Applicazione

Un tipico caso d'uso di questo algoritmo è trovare il minimo comune antenato di due nodi in un albero DOM. A volte abbiamo solo bisogno di collegare un listener di eventi per entrambe le voci (anche prima di essere attaccato al DOM!). Anche se questo evento per attaccare il "documento" funziona bene, di tutti gli elementi dai nodi fino alla radice sarà "catturare" questi eventi a causa di bubbling degli eventi.Quindi collegare l'evento al più basso antenato comune è una soluzione migliore.


Nessun commento:

Posta un commento

Nota. Solo i membri di questo blog possono postare un commento.