Binary Tree Rotation

голоса
3

Я работаю на реализацию дерева поиска AVL. До сих пор я закончил часть кодирования, и я начал испытывать его на ошибки. Я узнал, что мои методы вращения узла прослушиваются и ради бога, я не могу понять, в чем проблема.

Алгоритм работает как надо на бумаге, но при выполнении на машине это хорошо ... просачивается узлы дерева.

Это метод , используемый для вращения узла слева: 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;
}

В моем методе вставки я комментировал AVL балансировки части и вместо этого я просто пытаюсь повернуть вновь вставленный узел влево. Результат для вставки целых чисел в порядке возрастания: мое дерево содержит только начальный корень (первый узел вставляется) и все другие узлы утечки.

Любая помощь в определении проблемы высоко ценится, как я начинаю сходить с ума.

Для справки: если я не использую никаких поворотов дерево не будет течь узлы, и он работает как обычное несбалансированное бинарное дерево поиска (для вставки и поиска).

Изменить: Из-за комментарий AJG85, я добавлю наблюдение:

Я добавил PRINTF «проверки» методу деструктор avl_search_tree :: avl_tree_node, который будет печатать ключевое значение (в моем случае 32-битных целых чисел) перед очисткой и к способу вставки в avl_search_tree, который будет печатать ключ только что вставили.

Затем в EntryPoint программы я выделить avl_search_tree на куче и добавить ключи к нему в порядке возрастания, а затем удалить его.

С включенным AVL Балансировка я получаю следующий результат в терминале:

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

Это означает, thatall вставка была успешной, но только корень был удален.

С AVL Балансировка закомментирована работает как обычный бинарное дерево поиска. Терминал выход:

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

Это означает, что все правильно очищено.

Теперь ... как я пришел к выводу, что методы вращений являются вопросами? Под закомментированной АВЛИ балансировки подпрограммы я добавил строку, которая вращает каждый вновь вставленный узел влево. Результат? То же самое, как если подпрограмма AVL Балансировка была включена.

А что касается методы update_height (), не изменяет структуру дерева в любом случае.

Я надеюсь, что это будет уточнить его.

Изменить 2:

Чтобы прояснить еще несколько вещей, его как реализован 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 и _right_child являются указателями на avl_tree_node объекты, выделенные в куче.

Редактирование 3:

Благодаря 2 комментария AGJ85 я обнаружил проблему. В моей циклическом сдвиге методы, которые я забыл, что я на самом деле нужен обновить корневой указатель этого дерева на новый корень всякий раза, когда корень был сдвинут.

В основном корень дерева всегда указует на первый вставленный узел и без обновления указателя при необходимости, мои Чередовать методы будут просачиваться корнем нового дерева, который был фактически выполненным правильно. :)

Спасибо AGJ85!

Задан 02/08/2011 в 18:19
источник пользователем
На других языках...                            


3 ответов

голоса
2

EDIT - Черт , - я не видел , что этот вопрос уже решен (ответ на вопрос). Тем не менее, может быть , есть несколько советов , не ответить в этой стоимости утилизации.

Я не проверял тщательно, но я думаю, что вы собираетесь не так в этой линии ...

_right_child = new_root->_left_child;

и что проблема заключается в том , что вы , возможно, уже переписаны new_root->_left_childв линии ...

_parent->_left_child = new_root;

То, что я думаю, что вы должны сделать, это, в самом начале, есть блок локальных определений, таких как ...

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;

Затем использовать orig_локальные переменные в качестве источников для последующих заданий. Это экономит определенное количество беспокоиться о потоке данных с помощью различных указателей во время вращения. Оптимизатор должен избавиться от любой избыточной работы стоит беспокоиться о том, в этом, и не так много , что в любом случае.

Пару дополнительных очков ...

Во-первых, C ++ (и C) стандарты резервные идентификаторы с ведущими подчеркиванием, и с двойными символами подчеркивания. Он утверждал, что вы можете получить неожиданные взаимодействия со стандартными и компилятором поставляется библиотеки, если вы не уважаете, что - я думаю, что должен был бы быть макро-связанных идентификаторов членов, хотя. Завершающие подчёркивания OK - Я предпочитаю использовать их для включают охрану.

Общая конвенция для переменных - членов является добавление водительству mили m_. Еще более распространенным, вероятно, не имея никакого специального префикса или суффикса вообще.

Во-вторых, вы можете (или не может), легче реализовать AVL деревья, которые не имеют родительских связей, хранящихся в узлах. Я еще не реализовали AVL деревья: еще сам, но я реализовать красно-черные деревья когда-то. Ряд алгоритмов необходимо включить рекурсивный поиск в качестве первого шага - вы не можете просто сделать стандартный поиск, который запоминает найденный узел, но отбрасывает маршрут до этого узла. Однако, рекурсивная реализация не так уж плохо, и есть меньше указателей на жонглировать.

Наконец, общий совет - пытается «всухую» алгоритм , как это легко может сбить вас с толку , если вы строго не работать через него шаг за шагом, и проверить все источники информации , которые имеют отношение (я уже изменили это?) В каждый шаг. Это очень легко попасть в привычку пропускать некоторые детали для скорости. Полезная машина помощи всухую является запуск кода шага за шагом в отладчике, и посмотреть , если результаты на каждый шаге согласны с бумажной сухой перспективе.

EDIT - еще одно замечание - я не буду называть это подсказка , потому что я не уверен в этом контексте. Я обычно реализовать структуру данных узлов с простыми структурами - не сокрытие данных, мало , если любой из функций - членов. Большая часть коды хранятся отдельно от структуры данных, часто в классе «инструмента». Я знаю , что это ломает старый «форма рисует себя» принцип объектно - ориентированного программирования, но ИМО это лучше работает на практике.

Ответил 02/08/2011 в 20:34
источник пользователем

голоса
3

Благодаря 2 комментария AGJ85 я обнаружил проблему. В моей циклическом сдвиге методы, которые я забыл, что я на самом деле нужен обновить корневой указатель этого дерева на новый корень всякий раза, когда корень был сдвинут.

В основном корень дерева всегда указует на первый вставленный узел и без обновления указателя при необходимости, мои Чередовать методы будут просачиваться корнем нового дерева, который был фактически выполненным правильно. :)

Ответил 03/08/2011 в 10:03
источник пользователем

голоса
1

Я вижу, что вы нашли ошибку, которую вы искали в вашем коде. (Как вы говорите, вы не обновляя указатель корня дерева к новому корню, когда корень изменилась. Это общая парадигма списка и дерева Вставить / Удалить методы возвращают указатель на голову списка или корень дерева, и если вы помните, что парадигма не будет делать ошибку снова.)

На более высоком уровне зрения, метод я использовал , чтобы избежать проблем с AVL Tree или красно-черное дерево кодом вместо этого использовать дерево AA , который имеет аналогичные характеристики для них, используя O (N) пространства и O (журнал п) для вставки, удаления и поиска. Однако АА дерева значительно проще код.

Ответил 04/08/2011 в 16:55
источник пользователем

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