Binary Tree Rotation

stemmen
3

Ik ben bezig met het implementeren van een AVL Zoekboom. Tot dusver heb ik het coderende deel klaar en ik ben begonnen met het testen het voor bugs. Ik kwam erachter dat mijn knooppunt rotatie methoden worden afgeluisterd en in godsnaam Ik kan niet begrijpen wat is het probleem.

Het algoritme werkt zoals het zou moeten op papier, maar wanneer deze worden uitgevoerd op een machine het goed ... lekt boom knooppunten.

Deze methode wordt gebruikt om een knooppunt naar links te draaien: 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;
}

In mijn methode voor het invoegen merkte ik het AVL balanceren deel en in plaats daarvan Ik ben gewoon proberen om de nieuw ingevoegde knooppunt naar links te draaien. Het resultaat voor het invoegen van integers in oplopende volgorde: mijn boom bevat alleen de eerste root (eerste knooppunt geplaatst) en alle andere knooppunten zijn gelekt.

Alle hulp bij het identificeren van het probleem wordt zeer gewaardeerd als ik ben beginnen gek te gaan.

Voor de goede orde: als ik geen rotaties gebruik maken van de boom zal niet nodes lekken en het werkt als een normale ongebalanceerde binaire zoekboom (voor het inbrengen en lookup).

Edit: Door AJG85's opmerking die ik zal de opmerkingen toe te voegen:

Ik printf 'checks' toegevoegd aan de destructor methode van avl_search_tree :: avl_tree_node dat de sleutel-waarde (in mijn geval 32 bits gehele getallen) zal drukken voordat opruimen en om de insert methode van de avl_search_tree dat de sleutel gewoon ingebracht worden afgedrukt.

Toen in entrypoint van de programma's die ik toewijzen een avl_search_tree op de hoop en de toetsen toe te voegen in oplopende volgorde en vervolgens te verwijderen.

Met AVL Balancing ingeschakeld krijg ik de volgende output in de terminal:

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

Dat betekent thatall inserties waren succesvol, maar alleen de wortel is verwijderd.

Met de AVL Balancing uitgecommentarieerd het werkt als een normale binaire zoekboom. De terminal output is:

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

Dat betekent dat alles goed is opgeruimd.

Nu ... hoe heb ik tot de conclusie gekomen dat de rotatie methoden zijn de problemen? Onder gereageerd AVL balanceren subroutine een lijn die elke nieuw geplaatste knooppunt naar links roteert I toegevoegd. Het resultaat? Het zelfde als de AVL Balancing subroutine werd ingeschakeld.

En wat betreft de methode update_height (), Het is niet de structuur van de boom te veranderen in any way.

Ik hoop dat dit zal verduidelijken.

Probleem 2:

Om nog meer dingen te verduidelijken, zijn is hoe de avl_tree_node destructor is geïmplementeerd:

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 en _right_child zijn verwijzingen naar objecten op de heap toegewezen avl_tree_node.

Edit 3:

Dankzij 2e reactie AGJ85's vond ik de kwestie. In mijn rotate methoden vergat ik dat ik eigenlijk moet de boom root pointer updaten naar de nieuwe wortel wanneer de wortel werd verschoven.

In principe wortel van de boom werd altijd naar de eerste gestoken knooppunt en zonder het bijwerken van de wijzer als dat nodig is, zou mijn rotate methoden wortel van de nieuwe boom die eigenlijk recht is geconfigureerd lekken. :)

Dank je wel AGJ85!

De vraag is gesteld op 02/08/2011 om 18:19
bron van user
In andere talen...                            


3 antwoorden

stemmen
2

EDIT - Verdomme - heb ik niet gezien dat het probleem al is opgelost (antwoord in kwestie). Toch, misschien is er een aantal niet-antwoord tips in dit waard recycling.

Ik heb niet grondig gecontroleerd, maar ik denk dat je verkeerd gaan op deze lijn ...

_right_child = new_root->_left_child;

en dat het probleem is dat je misschien al overschreven new_root->_left_childin de lijn ...

_parent->_left_child = new_root;

Wat ik denk dat je moet doen is, bij de start, hebben een blok van lokale definities zoals ...

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;

Maak dan gebruik van de orig_lokale variabelen als bronnen voor latere opdrachten. Dit bespaart zekere zorgen over gegevensstroom via verschillende pointers tijdens de rotatie. De optimizer moet zich te ontdoen van overbodige werk de moeite waard zorgen te maken over deze, en er is niet veel van dat in ieder geval.

Een paar extra punten ...

Ten eerste, de C ++ (en C) standaarden reserve identificatiemiddelen met toonaangevende underscores, en met dubbel-underscores. Het wordt beweerd dat u verrassen interacties kunnen krijgen met de standaard en-compiler meegeleverde bibliotheken als je niet respecteren - Ik denk dat dat zou moeten macro-gerelateerde voor lid identifiers te zijn, dat wel. Trailing underscores zijn OK - Ik heb de neiging om ze te gebruiken voor onder andere bewakers.

Een algemene conventie voor lidvariabelen is om een leidende toe te voegen mof m_. Nog vaker voor, waarschijnlijk, is niet met een speciale voor- of achtervoegsel at all.

Ten tweede, kunt u (of niet) vinden het gemakkelijker om AVL bomen die niet beschikken over ouder banden opgeslagen in de knooppunten te implementeren. Ik heb niet geïmplementeerd AVL bomen nog mezelf, maar ik heb een keer uit te voeren rood-zwarte bomen. Een aantal algoritmes nodig om een ​​recursieve zoekopdracht als de eerste stap omvatten - je kunt niet gewoon een standaard onderzoek dat de gevonden knooppunt herinnert zich maar verwerpt die de route tot aan dat knooppunt. Echter, recursieve implementatie is niet al te slecht, en er is minder pointers jongleren.

Ten slotte is een algemene tip - proberen te "dry run" een algoritme als dit kan gemakkelijk struikelen u op, tenzij je strikt werken via het stap voor stap, en laat U alle bronnen van informatie die relevant zijn (heb ik al deze gewijzigde?) Bij elke stap. Het is heel makkelijk te krijgen in de gewoonte van het overslaan van een deel van de gegevens voor snelheid. Een handige-machine bijgestaan dry run is om de code stap-voor-stap in een debugger lopen, en te zien of de resultaten bij elke stap het eens met uw papieren drooglopen.

EDIT - nog een noot - zal ik niet noemen dit een tip, want ik ben niet zeker in deze context. Meestal voeren datastructuur knooppunten met eenvoudige structuren - geen gegevens verbergen, weinig of geen lidfuncties. Het grootste deel van de code wordt gescheiden gehouden van de datastructuur, vaak in een "tool" class. Ik weet dat dit breekt de oude "de vorm zelf trekt" OOP principe, maar IMO het beter werkt in de praktijk.

antwoordde op 02/08/2011 om 20:34
bron van user

stemmen
3

Dankzij 2e reactie AGJ85's vond ik de kwestie. In mijn rotate methoden vergat ik dat ik eigenlijk moet de boom root pointer updaten naar de nieuwe wortel wanneer de wortel werd verschoven.

In principe wortel van de boom werd altijd naar de eerste gestoken knooppunt en zonder het bijwerken van de wijzer als dat nodig is, zou mijn rotate methoden wortel van de nieuwe boom die eigenlijk recht is geconfigureerd lekken. :)

antwoordde op 03/08/2011 om 10:03
bron van user

stemmen
1

Ik zie dat je de bug die u zocht in de code hebt gevonden. (Zoals je zegt, was je niet updaten van de boomwortel aanwijzer naar het nieuwe wortel wanneer de wortel veranderd. Het is een veel voorkomende paradigma voor de lijst en boom invoegen / verwijderen methoden om een ​​pointer terug te keren naar het hoofd van de lijst of wortel van de boom, en als Herinnert u zich dat paradigma de fout niet meer zal maken.)

Op een hoger niveau van mening dat de techniek die ik heb gebruikt om problemen te vermijden met AVL Tree of Red-Black Tree code is om te gebruiken in plaats van een AA Tree , die vergelijkbare prestaties voor hen heeft, met behulp van O (n) ruimte en O (log n) tijd voor Insert, Delete, en Search. Echter, AA bomen zijn aanzienlijk eenvoudiger om code.

antwoordde op 04/08/2011 om 16:55
bron van user

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