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.
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.
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.
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.
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;
. 06
pubblica
$ 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.