2016-11-25 5 views
1

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

Из-за того, что элементы появляются в неуравновешенном дереве, мне кажется, что существует некоторая связь между обходным порядком (слева, по середине, справа).

+0

Если вы получили это задание в классе, я бы ожидал, что ваш преподаватель заранее предоставил достаточный материал, необходимый для выполнения этого задания; поэтому ваша собеседница, которой ваши вопросы должны быть продвинуты, будет вашим инструктором. –

+0

Это не присвоение класса @SamVarshavchik, я занимаюсь – C2K

+0

благодаря @melpomene, сделав это редактирование в вопросе – C2K

ответ

1
void unbalance(Node **pp) { 
    Node *p; 
    while ((p = *pp)) { 
     if (!p->left) { 
      pp = &p->right; 
     } else { 
      Node *tmp = p->left->right; 
      *pp = p->left; 
      p->left->right = p; 
      p->left = tmp; 
     } 
    } 
} 

... 
Node *tree = ...; 
unbalance(&tree); 

Этот код основан на ответе @ templatetypedef. Он использует указатель на указатель, чтобы избежать сиротских узлов (который также избавляется от finalRoot, единственной целью которого является избегать сирот первоначального корня, переданного вызывающим).

Идея состоит в том, чтобы спуститься по дереву вдоль самой правой ветви. Если никогда не осталось детей, мы вводим только первую часть инструкции if и выходим, ничего не делая.

Часть else используется, когда имеется левое поддерево. В этом случае мы поворачиваем дерево справа. animated tree rotation

Это приводит к вытягиванию одного узла из левого поддерева, то есть наше левое поддерево теперь на один узел меньше. Мы повторяем это до тех пор, пока левое поддерево полностью не исчезнет, ​​и в этот момент мы снова войдем в первую часть if. Затем мы переходим к остальной правой поддереве и повторяем все это.

Указатели на указатели часто полезны при изменении структуры связанного списка или дерева. Они позволяли нам работать непосредственно с оригинальными указателями, а не копировать их. В этом случае назначение *pp = ... изменяет указатель в другом месте (на который указывает pp). Это либо исходный корень (как передается вызывающим), либо один из указателей right внутри дерева (как установлено pp = &p->right). Это повторное назначение необходимо, потому что вращение дерева в ветке else перемещает узлы вокруг, поэтому то, что раньше было корнем поддерева, перемещается дальше вниз, а прежний левый узел подтягивается, чтобы стать новым корнем. Мы обновляем *pp, чтобы сохранить инвариант, указатель выше (либо переменная tree в нашем вызывающем абоненте, либо член right нашего родительского узла) указывает на (новый) корень поддерева.

+0

Большое вам спасибо! Это работало для реализации, с которой я работал. Не могли бы вы подробнее рассказать о своем использовании Узла ** и как это помогло? Это потому, что это ссылка для начала, и, следовательно, изменения действительно отражаются? Не могли бы вы также рассказать мне, какое программное обеспечение/веб-сайт вы использовали для анимации? – C2K

+0

@ C2K Я украл анимацию с https://en.wikipedia.org/wiki/Tree_rotation (на самом деле я просто ее связал). Он был создан «Tar-Elessar» и находится в [CC BY-SA 4.0] (http://creativecommons.org/licenses/by-sa/4.0). – melpomene

1

Один из способов сделать это итеративно - использовать вращения деревьев. Идея выглядит примерно так:

  1. Если корневой узел имеет левый дочерний элемент, поверните корневой узел вправо.
  2. В противном случае корневой узел не имеет левого дочернего элемента. Спуститесь направо и повторите этот процесс.

В псевдокоде:

Node finalRoot = null; 
while (currNode != null) { 
    if (currNode.left != null) { 
     currNode = rotateRight(currNode); 
    } else { 
     if (finalRoot == null) finalRoot = currNode; 
     currNode = currNode.right; 
    } 
} 
return finalRoot; 

Этот подход адаптирован из алгоритма День-Стаут-Уоррен, который делает это в качестве первого шага. Он работает во времени O (n).

+0

Что-то беспокоило меня о 'if (finalRoot == null)' part: Это неэлегантно. И теперь я знаю, почему: он обрабатывает исходный корневой указатель (начальное значение «currNode») по-разному от всех шагов указателя «currNode» другого (все дети '.right'). Я думаю, это означает, что псевдокод сломан: он не обновляет 'currNode.right', поэтому дерево теряет доступ ко всем левым узлам, которые были повернуты. – melpomene

+0

Итак,' finalRoot' на самом деле не является начальным корнем дерева - он заканчивается который является самым правым узлом в дереве (отслеживает код в дереве образцов). Кроме того, можете ли вы рассказать о потерях узлов? Когда мы спускаемся в правильное поддерево, мы знаем, что нет потерянного поддерева (и что все выше нас находится в гигантском позвоночнике). – templatetypedef

+0

Вот схема того, что я говорю: http://i.imgur.com /P1qPc1m.png - это имеет смысл? Дело в том, что 'A.right' никогда не обновляется и поэтому продолжает указывать на' B', даже когда 'B' больше не является корнем его поддерева. – melpomene

Смежные вопросы