Sono consentite chiavi duplicate nella definizione degli alberi di ricerca binari?


139

Sto cercando di trovare la definizione di un albero di ricerca binario e continuo a trovare definizioni diverse ovunque.

Alcuni sostengono che per ogni dato sottostruttura la chiave figlio sinistra sia inferiore o uguale alla radice.

Alcuni sostengono che per ogni dato sottostruttura la chiave figlio giusta sia maggiore o uguale alla radice.

E il mio vecchio libro sulle strutture di dati del college dice "ogni elemento ha una chiave e non due elementi hanno la stessa chiave".

Esiste una definizione universale di bst? Soprattutto per quanto riguarda cosa fare con gli alberi con più istanze della stessa chiave.

EDIT: Forse non ero chiaro, le definizioni che sto vedendo sono

1) sinistra <= radice <destra

2) sinistra <root <= destra

3) left <root <right, in modo tale che non esistano chiavi duplicate.

Risposte:


78

Molti algoritmi specificheranno che i duplicati sono esclusi. Ad esempio, gli algoritmi di esempio nel libro Algorithms del MIT di solito presentano esempi senza duplicati. È abbastanza banale implementare i duplicati (o come elenco sul nodo o in una direzione particolare).

La maggior parte (che ho visto) specifica i bambini di sinistra come <= e i bambini di destra come>. In pratica, un BST che consente a entrambi i bambini destro o sinistro di essere uguali al nodo principale, richiederà ulteriori passaggi computazionali per completare una ricerca in cui sono consentiti nodi duplicati.

È preferibile utilizzare un elenco nel nodo per memorizzare i duplicati, poiché l'inserimento di un valore '=' su un lato di un nodo richiede la riscrittura dell'albero su quel lato per posizionare il nodo come figlio, oppure il nodo viene posizionato come grande -bambino, ad un certo punto in basso, che elimina parte dell'efficienza della ricerca.

Devi ricordare, la maggior parte degli esempi di classe sono semplificati per ritrarre e fornire il concetto. Non valgono lo squat in molte situazioni del mondo reale. Ma l'affermazione, "ogni elemento ha una chiave e non due elementi hanno la stessa chiave", non è violata dall'uso di un elenco nel nodo dell'elemento.

Quindi segui quello che ha detto il tuo libro sulle strutture dati!

Modificare:

La definizione universale di un albero di ricerca binario implica l'archiviazione e la ricerca di una chiave basata sul passaggio di una struttura di dati in una delle due direzioni. In senso pragmatico, ciò significa che se il valore è <>, si attraversa la struttura dei dati in una delle due "direzioni". Quindi, in questo senso, i valori duplicati non hanno alcun senso.

Questo è diverso da BSP, o partizione di ricerca binaria, ma non così diverso. L'algoritmo da cercare ha una delle due direzioni per "viaggiare", o è fatto (con successo o no). Quindi mi scuso che la mia risposta originale non abbia affrontato il concetto di "definizione universale", poiché i duplicati sono in realtà un distinto argomento (qualcosa che hai a che fare con una ricerca riuscita, non come parte della ricerca binaria.)


1
Quali sono gli svantaggi dell'utilizzo di un elenco nel nodo?
Pacerier,

1
@Pacerier Penso che invece di mantenere un elenco, possiamo mantenere un conteggio di riferimento su ciascun nodo e aggiornare il conteggio quando si verificano duplicati. Un tale algoritmo sarebbe molto più semplice ed efficiente nella ricerca e nella memorizzazione. Inoltre, richiederebbe modifiche minime all'algoritmo esistente che non supporta i duplicati.
SimpleGuy

50

Se l'albero di ricerca binario è un albero nero rosso o se si intende eseguire qualsiasi tipo di "rotazione dell'albero", i nodi duplicati causeranno problemi. Immagina che la tua regola dell'albero sia questa:

left <root <= right

Ora immagina un albero semplice la cui radice è 5, il figlio sinistro è zero e il figlio destro è 5. Se fai una rotazione sinistra sulla radice, finisci con un 5 nel figlio sinistro e un 5 nella radice con il figlio destro essendo nullo. Ora qualcosa nella struttura a sinistra è uguale alla radice, ma la tua regola sopra presuppone che sia stata lasciata <radice.

Ho trascorso ore a cercare di capire perché i miei alberi rossi / neri occasionalmente si sarebbero spostati fuori servizio, il problema era quello che ho descritto sopra. Spero che qualcuno lo legga e si risparmi ore di debug in futuro!


18
Non ruotare quando hai nodi uguali! Passa al livello successivo e ruotalo.
Rich

2
Altre soluzioni sono modificare la regola dell'albero in modo che sia left <= node <= right, o inserire solo prima della prima occorrenza di un valore.
paxdiablo,

Quali problemi può causare in pratica? Mi sembra che se stai bene con left <= node <= right, allora tutte le operazioni dell'albero rosso-nero funzioneranno comunque.
Björn Lindqvist,

39

Tutte e tre le definizioni sono accettabili e corrette. Definiscono diverse varianti di un BST.

Il libro della struttura dei dati del tuo college non è riuscito a chiarire che la sua definizione non era l'unica possibile.

Certamente, consentire i duplicati aggiunge complessità. Se usi la definizione "left <= root <right" e hai un albero come:

      3
    /   \
  2       4

quindi l'aggiunta di una chiave duplicata "3" a questa struttura comporterà:

      3
    /   \
  2       4
    \
     3

Si noti che i duplicati non sono in livelli contigui.

Questo è un grosso problema quando si consentono i duplicati in una rappresentazione BST come quella sopra: i duplicati possono essere separati da un numero qualsiasi di livelli, quindi controllare l'esistenza di duplicati non è così semplice come controllare la presenza immediata di un nodo di un nodo.

Un'opzione per evitare questo problema è non rappresentare strutturalmente i duplicati (come nodi separati) ma utilizzare invece un contatore che conta il numero di occorrenze della chiave. L'esempio precedente avrebbe quindi un albero come:

      3(1)
    /     \
  2(1)     4(1)

e dopo aver inserito la chiave duplicata "3" diventerà:

      3(2)
    /     \
  2(1)     4(1)

Ciò semplifica le operazioni di ricerca, rimozione e inserimento, a scapito di alcuni byte aggiuntivi e operazioni del contatore.


Sono molto sorpreso che questo non sia mai stato menzionato nel libro di testo che sto usando. Nemmeno il prof lo ha menzionato, né il fatto che le chiavi duplicate siano persino un problema ...
Oloff Biermann,

22

In un BST, tutti i valori che scendono sul lato sinistro di un nodo sono inferiori (o uguali a, vedi più avanti) il nodo stesso. Allo stesso modo, tutti i valori che scendono sul lato destro di un nodo sono maggiori (o uguali a) del valore dei nodi (a) .

Alcuni BST possono scegliere di consentire valori duplicati, quindi i qualificatori "o uguali a" sopra.

L'esempio seguente può chiarire:

            |
      +--- 14 ---+
      |          |
+--- 13    +--- 22 ---+
|          |          |
1         16    +--- 29 ---+
                |          |
               28         29

Questo mostra un BST che consente duplicati: puoi vedere che per trovare un valore, inizi dal nodo principale e scendi la sottostruttura sinistra o destra a seconda che il tuo valore di ricerca sia minore o maggiore del valore del nodo.

Questo può essere fatto in modo ricorsivo con qualcosa del tipo:

def hasVal (node, srchval):
    if node == NULL:
         return false
    if node.val == srchval:
        return true
    if node.val > srchval:
        return hasVal (node.left, srchval)
    return hasVal (node.right, srchval)

e chiamandolo con:

foundIt = hasVal (rootNode, valToLookFor)

I duplicati aggiungono un po 'di complessità poiché potrebbe essere necessario continuare a cercare una volta trovato il valore per altri nodi dello stesso valore.


(a) Si potrebbe effettivamente quelli in direzione opposta se lo desidera a condizione di modificare la modalità di ricerca per una chiave specifica. Un BST deve solo mantenere un certo ordine, indipendentemente dal fatto che sia crescente o decrescente.


Per il caso duplicato, puoi semplicemente verificare se il figlio giusto è lo stesso del nodo corrente nella clausola node.val == srchval: e quindi se va bene?
inneggia l'

9

Nel libro "Introduzione agli algoritmi", terza edizione, di Cormen, Leiserson, Rivest e Stein, un albero di ricerca binario (BST) è esplicitamente definito come consentire duplicati . Questo può essere visto nella figura 12.1 e seguenti (pagina 287):

"Le chiavi in ​​un albero di ricerca binario sono sempre memorizzate in modo tale da soddisfare la proprietà dell'albero di ricerca binario: Sia xun nodo in un albero di ricerca binario. Se yc'è un nodo nella sottostruttura sinistra di x, allora y:key <= x:key. Se yè un nodo nel sottoalbero destro di x, allora y:key >= x:key."

Inoltre, un albero rosso-nero viene quindi definito a pagina 308 come:

"Un albero rosso-nero è un albero di ricerca binario con un ulteriore bit di memoria per nodo: il suo colore"

Pertanto, gli alberi rosso-neri definiti in questo libro supportano i duplicati.


4

Qualsiasi definizione è valida. Finché sei coerente nella tua implementazione (metti sempre nodi uguali a destra, mettili sempre a sinistra o non permetterli mai), allora stai bene. Penso che sia più comune non permetterli, ma è ancora un BST se sono autorizzati e posizionati a sinistra oa destra.


1
se si dispone di un set di dati che contiene chiavi duplicate, tali elementi devono essere archiviati all'interno del nodo 1 dell'albero mediante un metodo diverso (elenco collegato, ecc.). l'albero dovrebbe contenere solo chiavi univoche.
Nickf

1
Si noti inoltre dal wiki che la sottostruttura destra contiene valori "maggiori o uguali a" la radice. Quindi la definizione della wiki è contraddittoria.
SoapBox,

1
+1: persone diverse usano definizioni diverse. Se si implementa un nuovo BST, è necessario assicurarsi di essere espliciti su quali ipotesi si stanno formando su voci duplicate.
Fooz,

1
Sembra che il consenso sia (sinistra <= radice <= destra) quando si consentono i duplicati. Ma quella definizione di alcune persone di un BST non consente dups. O forse alcune persone lo insegnano in questo modo per evitare l'ulteriore complessità.
Tim Merrifield,

1
! errato è ORA sinistra <= radice <destra O sinistra <radice <= destra, O sinistra> radice> = destra O sinistra> = radice> destra
Mitch Wheat

3

Lavorando su un'implementazione dell'albero rosso-nero stavo riscontrando problemi nella convalida dell'albero con più chiavi fino a quando mi sono reso conto che con la rotazione dell'inserto rosso-nero, è necessario allentare il vincolo per

left <= root <= right

Poiché nessuno dei documenti che stavo cercando consentivano chiavi duplicate e non volevo riscrivere i metodi di rotazione per tenerne conto, ho semplicemente deciso di modificare i miei nodi per consentire più valori all'interno del nodo e nessuna chiave duplicata in l'albero.


2

Queste tre cose che hai detto sono tutte vere.

  • Le chiavi sono uniche
  • A sinistra sono le chiavi inferiori a questa
  • A destra sono le chiavi più grandi di questa

Suppongo che potresti invertire l'albero e posizionare i tasti più piccoli sulla destra, ma in realtà il concetto di "sinistra" e "destra" è proprio questo: un concetto visivo per aiutarci a pensare a una struttura di dati che non ha davvero una sinistra o giusto, quindi non importa davvero.


1

1.) left <= root <right

2.) left <root <= right

3.) left <root <right, in modo tale che non esistano chiavi duplicate.

Potrei dover andare a cercare i miei libri sugli algoritmi, ma dalla parte superiore della mia testa (3) c'è la forma canonica.

(1) o (2) si verificano solo quando si inizia a consentire nodi duplicati e si inseriscono nodi duplicati nell'albero stesso (anziché nel nodo contenente un elenco).


Potresti spiegare perché left <= root <= right non è l'ideale?
Helin Wang,

Dai un'occhiata alla risposta accettata da @paxdiablo - Puoi vedere il valore duplicato può esistere con >=. L'ideale dipende dalle tue esigenze, ma se hai molti valori duplicati e permetti ai duplicati di esistere nella struttura, il tuo bst può finire per essere lineare, ovvero O (n).
Robert Paulson,

1

Chiavi duplicate • Cosa succede se è presente più di un elemento dati con la stessa chiave? - Ciò presenta un leggero problema negli alberi rosso-neri. - È importante che i nodi con la stessa chiave siano distribuiti su entrambi i lati degli altri nodi con la stessa chiave. - Cioè, se le chiavi arrivano nell'ordine 50, 50, 50, • si desidera che il secondo 50 vada a destra del primo e il terzo 50 vada a sinistra del primo. • Altrimenti, l'albero diventa sbilanciato. • Questo potrebbe essere gestito da un qualche tipo di processo randomizzato nell'algoritmo di inserimento. - Tuttavia, il processo di ricerca diventa più complicato se tutti gli articoli con la stessa chiave devono essere trovati. • È più semplice bandire gli oggetti con la stessa chiave. - In questa discussione assumeremo che non siano consentiti duplicati

È possibile creare un elenco collegato per ciascun nodo dell'albero che contiene chiavi duplicate e memorizzare i dati nell'elenco.


1

Voglio solo aggiungere qualche informazione in più a ciò che ha risposto @Robert Paulson.

Supponiamo che il nodo contenga chiave e dati. Quindi i nodi con la stessa chiave potrebbero contenere dati diversi.
(Quindi la ricerca deve trovare tutti i nodi con la stessa chiave)

1) sinistra <= cur <destra

2) sinistra <cur <= destra

3) sinistra <= cur <= destra

4) left <cur <right && cur contiene nodi di pari livello con la stessa chiave.

5) left <cur <right, in modo tale che non esistano chiavi duplicate.

1) & 2) funziona bene se l'albero non ha alcuna funzione relativa alla rotazione per prevenire l'asimmetria.
Ma questo modulo non funziona con l' albero AVL o l' albero rosso-nero , perché la rotazione interromperà il principale.
E anche se search () trova il nodo con la chiave, deve attraversare il nodo foglia per i nodi con chiave duplicata.
Creare complessità temporale per search = theta (logN)

3) funzionerà bene con qualsiasi forma di BST con funzioni relative alla rotazione.
Ma la ricerca prenderà O (n) , rovinando lo scopo dell'uso di BST.
Supponiamo di avere l'albero come sotto, con 3) principal.

         12
       /    \
     10     20
    /  \    /
   9   11  12 
      /      \
    10       12

Se cerchiamo (12) su questo albero, anche se ne abbiamo trovati 12 alla radice, dobbiamo continuare a cercare sia il figlio sinistro che quello destro per cercare la chiave duplicata.
Questo richiede O (n) tempo come ho detto.

4) è il mio preferito personale. Diciamo che fratello indica il nodo con la stessa chiave.
Possiamo cambiare l'albero sopra in sotto.

         12 - 12 - 12
       /    \
10 - 10     20
    /  \    /
   9   11  12

Ora qualsiasi ricerca prenderà O (logN) perché non dobbiamo attraversare i figli per la chiave duplicata.
E questo principio funziona bene anche con l' albero AVL o RB .


0

La relazione di ordinamento degli elementi <= è un ordine totale, quindi la relazione deve essere riflessiva ma generalmente un albero di ricerca binario (aka BST) è un albero senza duplicati.

Altrimenti se ci sono duplicati devi eseguire due o più volte la stessa funzione di cancellazione!

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.