Обход и вставка в странное двоичное дерево - PullRequest
2 голосов
/ 21 сентября 2010

Хорошо, я столкнулся с этим вопросом о дереве, который выглядел простым, но начал сводить меня с ума.

Данное дерево похоже на двоичное дерево поиска, но с одним отличием:

  1. Для узла leftNode.value <значение <= rightNode.value. Однако (из-за отсутствия какой-либо дополнительной информации и из приведенного ниже примера) это применимо ТОЛЬКО к непосредственным дочерним элементам, а не к узлам под каждым дочерним элементом. Таким образом, возможно иметь значение где-то в левом поддереве, которое> = значение текущего узла.

Это НЕ самобалансирующееся дерево; существующая древовидная структура не будет изменена при вставке, кроме добавления нового значения в новый конечный узел. Не допускается переключение или смещение значений или узлов.

Также был приведен пример древовидной структуры после вставки тестовых значений:

If we insert 8, 5, 9, 3, 3, 2, 12, 4, 10:


   8


   8
  5  


   8
  5 9


   8
  5 9
 3


   8
  5 9
 3
  3


   8
  5 9
 3   
2 3


   8
  5 9
 3   12
2 3


   8
  5 9
 3   12
2 3
   4


    8
  5   9
 3  10 12
2 3
   4

Я предположил, что 10 был правым дочерним элементом 5, поскольку данное ограничение не позволяет ему быть левым дочерним элементом 9 *. 1014 *

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

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

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

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

РЕДАКТИРОВАТЬ: Проблема решена. Это была опечатка, представленная тестировщиком. Это должно было быть BST, но «10» в примере было помещено в неправильную позицию; это должен был быть левый ребенок "12".

Ответы [ 2 ]

3 голосов
/ 21 сентября 2010

Те примеры имеют смысл, пока не появится 10. Основное предположение: это опечатка. Но он не может (и не смотрит) быть ребенком 5 лет. Это левый ребенок 9 лет.

Но это также первое, что потребовало бы реструктуризации Дерева (чтобы оно поместилось между 9 и 12). Мне кажется, что последняя картина на полпути такой реструктуризации. Мы видим конец истории?

Редактировать

Хорошо, я не полностью прочитал правило 2. Похоже, что 10 должно было стать левым потомком 12. У вставки нет веских оснований для того, чтобы взять левую ветвь в корне.

Если 10 является потомком 5 или 9, он перестает быть BST или чем-то еще, кроме неупорядоченного двоичного дерева.

1 голос
/ 21 сентября 2010

Редактировать

Если подумать, я более склонен полагать, что в вашем примере есть опечатка, а не BST. Расположение 10 не имеет смысла. Вплоть до тех пор это совершенно действительный BST. Если бы 10 были вставлены в качестве левого листа узла 12, тогда BST-сущность была бы сохранена.

Если leftNode.value < value <= rightNode.value применяется только к непосредственным потомкам, а не ко всем потомкам, то это бесполезная структура данных. Я имею в виду, я думаю, что его можно использовать, но, поскольку вы в конечном итоге пройдете по всему дереву как при вставках, так и при поиске, это кажется довольно бессмысленным.


Я думаю, что схема вашего алгоритма вставки будет выглядеть следующим образом: псевдопифон. Суть в том, чтобы попытаться добавить узел в виде листа, где это возможно, или вставить его в любое поддерево. Влево или вправо, это не имеет значения.

По сути, мы просто смотрим вниз по всему дереву, где новое значение может быть добавлено как листовой узел. Как вы говорите, критерий порядка относится только к непосредственным детям. Вы должны быть придирчивы к добавлению новых листовых узлов. Но вы пробуете левое и правое поддеревья без разбора.

class TreeNode:
    def insert(self, value):
        # Can we add it as the left leaf of this node?
        if self.left is None and value < self.value:
            self.left = TreeNode(value)
            return True

        # Can we add it as the right leaf of this node?
        if self.right is None and value >= self.value:
            self.right = TreeNode(value)
            return True

        # Can we add it somewhere down the left sub-tree?
        if self.left.insert(value):
            return True

        # Can we add it somewhere down the right sub-tree?
        if self.right.insert(value):
            return True

        # Didn't find anywhere to add the node.
        return False

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

Представьте, что существует метод insert_depth, который возвращает глубину, в которую узел будет вставлен на , если он был вставлен в определенное поддерево, или возвращает & infin; если вставка невозможна. Опять же, это просто псевдокод:

left_depth  = self.left .insert_depth(value)
right_depth = self.right.insert_depth(value)

if left_depth < right_depth:
    return self.left .insert(value)
else:
    return self.right.insert(value)

В реальном коде я бы написал "если бы вы собирались вставить узел, куда бы вы его вставили?" вспомогательный метод. Запустите этот метод для обоих поддеревьев, сравните точки вставки и выберите одну (т.е. самую мелкую). Код будет немного неловким, но он избавит вас от обхода каждого поддерева дважды.

...