Arborele binar de rotație

voturi
3

Lucrez la punerea în aplicare a unui căutare AVL copac. Până în prezent, am terminat partea de codificare și am început să-l testarea pentru bug-uri. Am aflat ca nodul meu metode de rotație sunt bugged și pentru numele lui Dumnezeu nu pot să înțeleg ce e problema.

Algoritmul funcționează așa cum trebuie pe hârtie, dar atunci când este executat pe o mașină de ea bine ... scurgeri de informații noduri de arbore.

Aceasta este metoda folosită pentru a roti un nod la stânga: 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;
}

În metoda mea de inserare am comentat AVL de echilibrare parte în schimb eu doar încerc să se rotească nodul nou inserat în stânga. Rezultatul pentru inserarea numere întregi în ordine crescătoare: copacul meu conține doar rădăcina inițială (primul nod inserat) și toate celelalte noduri sunt scurgeri.

Orice ajutor în identificarea problemei este foarte apreciat ca am început să merg nebun.

Pentru înregistrare: dacă nu folosesc nici un rotațiile copac nu se va scurge noduri și funcționează ca un arbore binar de căutare dezechilibrată normală (pentru inserare și de căutare).

Editare: Din cauza lui AJG85 comentariu Voi adăuga observațiile:

Am adăugat „controale“ printf la metoda destructor de avl_search_tree :: avl_tree_node care va imprima valoarea cheii (în cazul meu 32 de numere întregi de biți) înainte de curățare și, la metoda de inserție a avl_search_tree care va imprima cheia tocmai introdusă.

Apoi, în punctul de intrare a programului am aloca un avl_search_tree pe heap și adăugați-l cheile în ordine crescătoare și apoi ștergeți-l.

Cu AVL Echilibrare activat I a lua următoarea ieșire în 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

Ceea ce înseamnă că thatall inserții au avut succes, dar numai rădăcina a fost ștearsă.

Cu AVL de echilibrare a comentat faptul că funcționează ca un arbore binar de căutare normală. Ieșirea terminalului este:

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

Ceea ce înseamnă că totul este curățat în mod corespunzător în sus.

Acum ... Cum am ajuns la concluzia că metodele de rotație sunt problemele? Sub subrutina AVL de echilibrare a comentat, am adăugat o linie care se rotește în fiecare nod nou inserat în stânga. Rezultatul? La fel ca și în cazul în care subrutina AVL de echilibrare a fost activată.

Și în ceea ce privește metoda update_height (), nu modifică structura copacului în nici un fel.

Sper că acest lucru se va clarifica.

Edit 2:

Pentru a clarifica unele mai multe lucruri, lui este modul în care este pus în aplicare destructor avl_tree_node:

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 și _right_child sunt indicii pentru a avl_tree_node obiecte alocate pe movila.

Editare 3:

Datorită doilea comentariu AGJ85 lui am găsit problema. În metodele mele Alternați am uitat că am de fapt, să actualizeze indicatorul rădăcină de copac la rădăcină nouă ori de câte ori a fost mutat de la rădăcină.

Practic rădăcina copacului a fost întotdeauna îndreptat spre primul nod inserat și fără a actualiza indicatorul atunci când este necesar, metodele mele de rotire se va scurge rădăcină noul copac, care a fost de fapt configurat corect. :)

Vă mulțumim AGJ85!

Întrebat 02/08/2011 la 18:19
sursa de către utilizator
În alte limbi...                            


3 răspunsuri

voturi
2

EDIT - La naiba - nu am vedea că problema este deja rezolvată (răspunsul în cauză). Totuși, poate că există câteva sfaturi de bază non-răspuns în această valoare salvatoare.

Nu am verificat bine, dar cred că te duci greșit la această linie ...

_right_child = new_root->_left_child;

și că problema este că este posibil să fi suprascrise deja new_root->_left_childîn linia ...

_parent->_left_child = new_root;

Ceea ce cred că ar trebui să faceți este, la început, au un bloc de definiții locale, cum ar fi ...

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;

Apoi , utilizați orig_variabilele locale ca sursele pentru misiuni ulterioare. Aceasta salvează o anumită cantitate de griji flux de date prin diferite pointerii în timpul rotației. Optimiser ar trebui să scape de orice lucru redundant în valoare de griji în acest lucru, și nu există o mare parte din asta , oricum.

Un cuplu de puncte în plus ...

În primul rând, C ++ (și C) identificatorii standarde de rezervă cu subliniere de conducere și cu dublu subliniere. Este susținut că puteți obține interacțiuni surpriză cu bibliotecile standard și furnizate compilator, dacă nu respecți asta - cred că ar trebui să fie macro-legate de identificatorii membre, totuși. subliniere rămase în urmă sunt OK - am tendinta de a le folosi pentru includ gărzi.

O convenție comună pentru variabile membru este de a adăuga un lider msau m_. Chiar mai frecvente, probabil, nu are nici un prefix special sau sufix la toate.

În al doilea rând, s-ar putea (sau nu poate) consideră că este mai ușor să pună în aplicare arbori AVL, care nu au legături mamă stocate în nodurile. Nu m-am pus în aplicare copaci AVL încă mine, dar am făcut-o pună în aplicare copaci roșu-negru o dată. Un număr de algoritmi trebuie să includă o căutare recursiv ca prim pas - nu se poate face doar o căutare standard care amintește nodul găsit, dar elimină traseul până la acel nod. Cu toate acestea, punerea în aplicare recursiv nu este prea rău, și există mai puține indicii pentru a jongla.

În cele din urmă, un sfat general - încercarea de a „seca“ un algoritm ca acest lucru se poate excursie cu ușurință dacă nu strict lucrați prin ea pas cu pas, și verificați toate sursele de informații care sunt relevante (am modificat deja acest lucru?) La fiecare pas. Este foarte ușor pentru a obține în obiceiul de a sări peste unele detalii pentru viteza. Un seca util asistata de mașină este de a rula codul pas cu pas într - un program de depanare, și a vedea dacă rezultatele la fiecare pas sunt de acord cu hârtia uscată rula.

EDIT - mai multe note - Nu voi numi un sfat pentru că nu sunt sigur că în acest context. Am să pună în aplicare , de obicei , noduri structura de date cu structs simplu - fără ascunderea unor date, dacă are câteva funcții de membru. Cea mai mare parte codul este păstrat separat de structura de date, de multe ori într - o clasă „instrument“. Știu că acest lucru se rupe vechi „forma în sine atrage“ principiul OOP, dar OMI funcționează mai bine în practică.

Publicat 02/08/2011 la 20:34
sursa de către utilizator

voturi
3

Datorită doilea comentariu AGJ85 lui am găsit problema. În metodele mele Alternați am uitat că am de fapt, să actualizeze indicatorul rădăcină de copac la rădăcină nouă ori de câte ori a fost mutat de la rădăcină.

Practic rădăcina copacului a fost întotdeauna îndreptat spre primul nod inserat și fără a actualiza indicatorul atunci când este necesar, metodele mele de rotire se va scurge rădăcină noul copac, care a fost de fapt configurat corect. :)

Publicat 03/08/2011 la 10:03
sursa de către utilizator

voturi
1

Văd că am găsit microfonul pe care o căutați în cod. (Așa cum ai spus, nu au fost actualizați indicatorul rădăcină copac la noua rădăcină atunci când rădăcina sa schimbat. Este o paradigmă comună pentru lista și copac Inserare / Ștergere metode pentru a returna un pointer la cap de listă sau de rădăcină de copac, și dacă vă amintiți că paradigma nu va face din nou greșeala.)

La un nivel mai ridicat de vedere, tehnica am folosit pentru a evita problemele cu AVL copac sau roșu-negru copac cod este de a utiliza în schimb un arbore AA , care are o performanță similară cu ei, folosind O (n) spațiu și O (log n) pentru Inserare, Ștergere și Căutare. Cu toate acestea, copaci AA sunt semnificativ mai simplu de cod.

Publicat 04/08/2011 la 16:55
sursa de către utilizator

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