Drzewo binarne Obrót

głosy
3

Pracuję na wdrożenie Search drzewo AVL. Dotychczas Skończyłem część kodującą i zacząłem testowanie go na błędy. I okazało się, że moje metody rotacji węzłowe są na podsłuchu i na litość boską nie rozumiem w czym problem.

Algorytm działa tak jak powinien na papierze, ale gdy są wykonywane na komputerze, to dobrze ... przecieki węzłów drzewa.

Jest to metoda stosowana do obracania węzeł w lewo: 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;
}

W moim metoda wstawiania I skomentował AVL część równoważąca i zamiast po prostu próbuję obrócić nowo wstawionego węzła w lewo. Wynik do wprowadzania liczb całkowitych w kolejności rosnącej: moje drzewo zawiera tylko początkową root (pierwszy węzeł włożona) i wszystkie inne węzły są wyciekły.

Każda pomoc w identyfikacji problemu jest wysoko ceniona jako Zaczynam się zwariować.

Dla przypomnienia: jeśli nie używam żadnych obrotów drzewo nie będzie przeciekać węzły i działa jak normalny niezrównoważonego binarne drzewo poszukiwań (do wkładania i odnośnika).

Edit: W związku z komentarzem AJG85 za dodam uwagi:

Dodałem printf „kontroli” metody destruktora avl_search_tree :: avl_tree_node że będzie drukować wartość klucza (w moim przypadku 32-bitowych liczb całkowitych) przed czyszczeniem i metody insert z avl_search_tree że będzie drukować tylko klucz wstawiony.

Następnie w punkt_wejścia programu I przydzielić avl_search_tree na stercie i dodać do niego klucze w kolejności rosnącej, a następnie usunąć.

Przy włączonej AVL Balancing uzyskać następujący wynik w terminalu:

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

Co oznacza thatall wstawki były udane, ale tylko korzeń został usunięty.

Z AVL Wyważanie wykomentowane działa jak normalny binarne drzewo poszukiwań. Wyjście terminal jest:

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

Co oznacza, że ​​wszystko jest prawidłowo czyszczone.

Teraz ... jak ja dochodzę do wniosku, że metody rotacji są problemy? Zgodnie z komentarzem podprogramu bilansującego AVL dodałem linię, która obraca każde nowo wstawionego węzła w lewo. Wynik? Tak samo jak wtedy, gdy podprogram AVL Balancing została włączona.

Oraz dotyczące sposobu update_height (), to nie zmienia strukturę drzewa w jakikolwiek sposób.

Mam nadzieję, że będzie to wyjaśnić.

Edit 2:

Aby wyjaśnić jeszcze kilka rzeczy, jak jest jego destruktor avl_tree_node jest realizowany:

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 są wskaźnikami do avl_tree_node obiektów przydzielone na stercie.

Edycja 3:

Dzięki AGJ85 za 2 komentarzu znalazłem problem. Obracanie w moich metodach Zapomniałam, że rzeczywiście trzeba zaktualizować drzewa za główny wskaźnik do nowego korzenia gdy korzeń został przesunięty.

Zasadniczo korzeń drzewa był zawsze wskazując pierwszego wstawionego węzła i bez aktualizacji wskaźnika, gdy są potrzebne, by moi Rotate metody wyciek pierwiastek nowe drzewo, które zostało właściwie skonfigurowane w prawo. :)

Dziękuję AGJ85!

Utwórz 02/08/2011 o 18:19
źródło użytkownik
W innych językach...                            


3 odpowiedzi

głosy
2

EDIT - Cholera - I nie zobaczysz, że problem został już rozwiązany (odpowiedź na pytanie). Mimo to, a może istnieje kilka wskazówek non-odpowiedź w tej cenie ratownictwo.

Nie dokładnie sprawdzane, ale myślę, że dzieje się źle w tej linii ...

_right_child = new_root->_left_child;

i że problemem jest to, że można już nadpisane new_root->_left_childw linii ...

_parent->_left_child = new_root;

Co myślę, że należy zrobić to na początku, mieć blok lokalnych definicji takich jak ...

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;

Następnie za pomocą orig_zmiennych lokalnych jako źródła do późniejszych zadań. Oszczędza to pewna ilość martwić przepływu danych przez różne wskaźniki podczas obrotu. Optymalizator powinien pozbyć się nadmiarowych prac warto martwić się tym, i nie ma wiele z tym w każdym razie.

Kilka dodatkowych punktów ...

Po pierwsze, C ++ (i C) Identyfikatory rezerwowe standardy z wiodących podkreślenia oraz z podwójnymi podkreśleniami. To twierdził, że można uzyskać interakcje zaskoczyć standardowych kompilatorów i bibliotek dostarczone jeśli nie szanuję - Chyba że chcesz być związane makro dla identyfikatorów członkowskich, choć. Spływu podkreślenia są OK - I mają tendencję do korzystania z nich na to strażników.

Wspólna konwencja dla zmiennych składowych jest dodanie wiodącej mlub m_. Jeszcze bardziej powszechne, prawdopodobnie, nie mając żadnej specjalnej przedrostek lub przyrostek w ogóle.

Po drugie, może (ale nie musi) być łatwiejsze do wdrożenia AVL drzew, które nie mają łącza nadrzędne przechowywanych w węzłach. I nie wdrożyły jeszcze drzew AVL siebie, ale ja wdrożyć czerwono-czarny drzew raz. Wiele algorytmów muszą zawierać rekurencyjne przeszukiwanie jako pierwszy krok - nie można po prostu zrobić standardowe wyszukiwanie że pamięta znaleziony węzeł ale odrzuca drogę w dół do tego węzła. Jednak rekurencyjna realizacja nie jest tak źle, i nie mniej wskaźniki żonglować.

Wreszcie, ogólna wskazówka - stara się „na sucho” algorytm tak łatwo potknąć cię chyba ściśle współpracować przez to krok po kroku, i sprawdzić wszystkie źródła informacji, które są istotne (ja już modyfikowany jest?) Na każdy krok. Jest bardzo łatwo dostać się do zwyczaju pomijanie niektórych szczegółów dotyczących prędkości. Przydatna maszyna wspomagane sucho jest uruchomienie kodu krok po kroku w debugger, i sprawdzić, czy wyniki na każdym kroku uzgodnić z papieru sucho.

EDIT - jeszcze jedna uwaga - nie będę nazywają to wskazówka, bo nie jestem pewien, w tym kontekście. I zazwyczaj wdrożyć węzły struktury danych z prostych strukturach - bez ukrywania danych, kilka jeśli żadnych funkcji składowych. Większość kodu jest oddzielona od struktury danych, często w klasie „narzędziem”. Wiem, że to łamie starej „kształt rysuje się” zasady OOP, ale IMO to działa lepiej w praktyce.

Odpowiedział 02/08/2011 o 20:34
źródło użytkownik

głosy
3

Dzięki AGJ85 za 2 komentarzu znalazłem problem. Obracanie w moich metodach Zapomniałam, że rzeczywiście trzeba zaktualizować drzewa za główny wskaźnik do nowego korzenia gdy korzeń został przesunięty.

Zasadniczo korzeń drzewa był zawsze wskazując pierwszego wstawionego węzła i bez aktualizacji wskaźnika, gdy są potrzebne, by moi Rotate metody wyciek pierwiastek nowe drzewo, które zostało właściwie skonfigurowane w prawo. :)

Odpowiedział 03/08/2011 o 10:03
źródło użytkownik

głosy
1

Widzę, że znalazłeś błąd, czego szukasz w kodzie. (Jak można powiedzieć, że nie zostały aktualizowanie wskaźnik korzenia drzewa do nowego pierwiastka, gdy korzeń zmieniło. Jest to wspólny paradygmat liście i drzewa Wstaw / Usuń metody zwracają wskaźnik do głowy listy lub korzenia drzewa, a jeśli pamiętać, że paradygmat nie będzie ponownie popełnić błąd).

Na wyższym poziomie widzenia, technika Użyłem aby uniknąć problemów z Drzewo AVL lub drzewo czerwono-czarne kod jest zamiast używać Drzewo AA , który ma podobną wydajność do nich, używając O (n) miejsca i O (log n) czas na wstawianie, usuwanie i wyszukiwanie. Jednak drzewa AA są znacznie prostsze w kodzie.

Odpowiedział 04/08/2011 o 16:55
źródło użytkownik

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