Binary albero di rotazione

voti
3

Sto lavorando per l'attuazione di una ricerca albero AVL. Finora ho finito la parte di codifica e ho cominciato a testarlo per i bug. Ho scoperto che i miei nodi metodi di rotazione vengono ostacolate e per l'amor di Dio, non riesco a capire qual è il problema.

L'algoritmo funziona come dovrebbe sulla carta, ma quando eseguito su una macchina bene ... perdite nodi dell'albero.

Questo è il metodo utilizzato per ruotare un nodo a fianco: http://pastebin.com/mPHj29Af

bool avl_search_tree::avl_tree_node::rotate_left()
{
    if (_right_child != NULL) {
        avl_tree_node *new_root = _right_child;
 
        if (_parent != NULL) {
            if (_parent->_left_child == this) {
                _parent->_left_child = new_root;
            } else {
                _parent->_right_child = new_root;
            }
        }
 
        new_root->_parent = _parent;
        _parent = new_root;
 
        _right_child = new_root->_left_child;
        new_root->_left_child = this;
 
        if (_right_child != NULL) {
            _right_child->_parent = this;
        }
 
        //update heights
        update_height();
        new_root->update_height();
 
        return true;
    }
 
    return false;
}

Nel mio metodo di inserimento ho commentato l'AVL bilanciamento parte e invece sto solo cercando di ruotare il nodo appena inserito a sinistra. Il risultato per l'inserimento interi in ordine crescente: il mio albero ha solo la radice iniziale (primo nodo inserita) e tutti gli altri nodi si perdono.

Qualsiasi aiuto per identificare il problema è molto apprezzato come sto cominciando a impazzire.

Per la cronaca: se io non uso alcun rotazioni l'albero non colerà nodi e funziona come un normale sbilanciato albero binario di ricerca (per l'inserimento e ricerca).

Edit: Grazie al commento di AJG85 io aggiungo le osservazioni:

Ho aggiunto printf 'controlli' al metodo distruttore di avl_search_tree :: avl_tree_node che stamperà il valore della chiave (nel mio caso 32 numeri interi bit) prima di pulizia e, al metodo di inserimento del avl_search_tree che verrà stampata la chiave appena inserito.

Poi nel entrypoint del programma ho allocare un avl_search_tree sul mucchio e aggiungi le chiavi ad esso in ordine crescente e quindi eliminarlo.

Con AVL Balancing abilitato ottengo il seguente output nel terminale:

bool avl_search_tree::insert(const int&) : 1
bool avl_search_tree::insert(const int&) : 2
bool avl_search_tree::insert(const int&) : 3
bool avl_search_tree::insert(const int&) : 4
bool avl_search_tree::insert(const int&) : 5
bool avl_search_tree::insert(const int&) : 6
bool avl_search_tree::insert(const int&) : 7
bool avl_search_tree::insert(const int&) : 8
avl_search_tree::avl_tree_node::~avl_tree_node() : 1

Il che significa thatall inserimenti hanno avuto successo, ma solo la radice è stato eliminato.

Con l'AVL Bilanciamento commentata funziona come un normale albero binario di ricerca. Il terminale di uscita è:

bool avl_search_tree::insert(const int&) : 1
bool avl_search_tree::insert(const int&) : 2
bool avl_search_tree::insert(const int&) : 3
bool avl_search_tree::insert(const int&) : 4
bool avl_search_tree::insert(const int&) : 5
bool avl_search_tree::insert(const int&) : 6
bool avl_search_tree::insert(const int&) : 7
bool avl_search_tree::insert(const int&) : 8
avl_search_tree::avl_tree_node::~avl_tree_node() : 1
avl_search_tree::avl_tree_node::~avl_tree_node() : 2
avl_search_tree::avl_tree_node::~avl_tree_node() : 3
avl_search_tree::avl_tree_node::~avl_tree_node() : 4
avl_search_tree::avl_tree_node::~avl_tree_node() : 5
avl_search_tree::avl_tree_node::~avl_tree_node() : 6
avl_search_tree::avl_tree_node::~avl_tree_node() : 7
avl_search_tree::avl_tree_node::~avl_tree_node() : 8

Il che significa che tutto sia ben ripulito.

Ora ... Come sono arrivato alla conclusione che i metodi di rotazione sono i problemi? Sotto la subroutine bilanciamento AVL commentato ho aggiunto una linea che ruota ogni nodo inserito a fianco. Il risultato? Lo stesso come se la subroutine AVL Balancing è stato attivato.

E per quanto riguarda il metodo update_height (), Esso non altera la struttura del albero in alcun modo.

Spero che questo permetterà di chiarire esso.

Edit 2:

Per chiarire alcune cose, la sua è come il distruttore avl_tree_node viene implementato:

avl_search_tree::avl_tree_node::~avl_tree_node()
{
    printf(%s : %d\n, __PRETTY_FUNCTION__, *_key);

    if (_left_child != NULL) {
        delete _left_child;
    }

    if (_right_child != NULL) {
        delete _right_child;
    }

    if (_key != NULL) {
        delete _key;
    }
}

_left_child e _right_child sono puntatori a avl_tree_node oggetti allocati sul mucchio.

Edit 3:

Grazie al 2 commento di AGJ85 ho trovato il problema. Nei miei metodi di rotazione ho dimenticato che io in realtà devo aggiornare puntatore radice dell'albero alla nuova radice ogni volta che la radice è stata spostata.

Fondamentalmente radice dell'albero era sempre rivolto al primo nodo inserito e senza aggiornare il puntatore quando necessario, miei metodi ruotare avrebbero perdite radice del nuovo albero che è stato effettivamente configurato destra. :)

Grazie AGJ85!

È pubblicato 02/08/2011 alle 18:19
fonte dall'utente
In altre lingue...                            


3 risposte

voti
2

EDIT - Dannazione - non ho visto che il problema è già risolto (la risposta in questione). Eppure, forse c'è qualche consiglio non risposta in questo vale la pena recupero.

Non ho controllato a fondo, ma penso che si sta andando male in questa linea ...

_right_child = new_root->_left_child;

e che il problema è che si può avere già sovrascritto new_root->_left_childnella linea di ...

_parent->_left_child = new_root;

Quello che penso che si dovrebbe fare è, all'inizio, dispone di un blocco di definizioni locali come ...

avl_tree_node *orig_parent      = _parent;
avl_tree_node *orig_this        = this;
avl_tree_node *orig_left_child  = _left_child;
avl_tree_node *orig_right_child = _right_child;

Quindi utilizzare i orig_variabili locali come le fonti per le assegnazioni successive. Ciò consente di risparmiare una certa quantità di preoccuparsi di flusso di dati attraverso i vari puntatori durante la rotazione. L'ottimizzatore dovrebbe sbarazzarsi di qualsiasi lavoro ridondante vale la pena preoccuparsi in questo, e non c'è molto di che comunque.

Un paio di punti extra ...

In primo luogo, il C ++ (e C) identificatori standard di riserva con i più importanti sottolineature, e con Double-sottolineatura. E 'sostenuto che si può ottenere interazioni a sorpresa con le librerie standard e compilatore fornito se non si rispettano, che - immagino che avrei dovuto essere macro-correlato per gli identificatori membri, però. sottolineature Trailing sono OK - Io tendo ad usare loro per comprendere le guardie.

Una convenzione comune per le variabili membro è quello di aggiungere un leader mom_ . Ancora più comuni, probabilmente, non sta avendo alcun prefisso speciale o suffisso a tutti.

In secondo luogo, si può (o non può) trovare più facile da implementare alberi AVL che non hanno legami genitore memorizzati nei nodi. Non ho implementato alberi AVL ancora me stesso, ma ho fatto implementare alberi rosso-neri una volta. Un certo numero di algoritmi bisogno di includere una ricerca ricorsiva come il primo passo - non si può semplicemente fare una ricerca standard che ricorda il nodo trovato, ma scarta il percorso fino a quel nodo. Tuttavia, implementazione ricorsiva non è male, e c'è un minor numero di puntatori a destreggiarsi.

Infine, un suggerimento generale - cercando di "funzionamento a secco", un algoritmo come questo può facilmente viaggio fino a meno che non strettamente lavora attraverso un passo alla volta, e controllare tutte le fonti di informazione che sono rilevanti (hanno già modificato questa?) A ogni passo. E 'molto facile da prendere l'abitudine di saltare alcuni dei dettagli per la velocità. Un funzionamento a secco utile assistito macchina è quello di eseguire il codice passo-passo in un debugger, e vedere se i risultati ad ogni passo d'accordo con la vostra carta di funzionamento a secco.

EDIT - ancora una nota - non voglio chiamare questo un suggerimento perché non sono sicuro che in questo contesto. Io di solito implementare nodi della struttura dati con semplici le strutture - senza nascondere i dati, poche o nessuna le funzioni membro. La maggior parte del codice è tenuto separato dalla struttura di dati, spesso in una classe di "funzione". So che questo rompe il vecchio "la forma stessa disegna" principio di programmazione orientata agli oggetti, ma IMO funziona meglio nella pratica.

Risposto il 02/08/2011 a 20:34
fonte dall'utente

voti
3

Grazie al 2 commento di AGJ85 ho trovato il problema. Nei miei metodi di rotazione ho dimenticato che io in realtà devo aggiornare puntatore radice dell'albero alla nuova radice ogni volta che la radice è stata spostata.

Fondamentalmente radice dell'albero era sempre rivolto al primo nodo inserito e senza aggiornare il puntatore quando necessario, miei metodi ruotare avrebbero perdite radice del nuovo albero che è stato effettivamente configurato destra. :)

Risposto il 03/08/2011 a 10:03
fonte dall'utente

voti
1

Vedo che hai trovato il bug che cercavate nel codice. (Come dici tu, non eri aggiornando il puntatore radice alla nuova radice quando la radice è cambiato. E 'un paradigma comune per la lista e albero Inserisci / Elimina i metodi per restituire un puntatore alla testa della lista o radice di albero, e se vi ricordate quel paradigma non farà di nuovo l'errore.)

A un livello superiore di vista, la tecnica che ho usato per evitare problemi con AVL albero o Red-Black Tree codice è utilizzare invece un albero AA , che ha prestazioni simili a loro, utilizzando O (n) spazio e O (log n) per inserire, eliminare e Search. Tuttavia, gli alberi AA sono notevolmente più semplice al codice.

Risposto il 04/08/2011 a 16:55
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more