Python — преобразование n-арного дерева в двоичное дерево

class Tree:

        def __init__(self, new_key):
    self.__key = new_key    # Root key value
    self.__children = []     # List of children
    self.__num_of_descendants = 0 # Number of Descendants of this node    

# Prints the given tree
def printTree(self):
    return self.printTreeGivenPrefix("", True)   

# Prints the given tree with the given prefix for the line
# last_child indicates whether the node is the last of its parent"s child
# or not
def printTreeGivenPrefix(self, line_prefix, last_child):
    print(line_prefix, end="")
    if last_child:
        print("â””--> ", end="")
    else:
        print("|--> ", end="")
    print(self.__key)

    if len(self.__children) > 0:
        next_pre = line_prefix
        if last_child:
            next_pre += "     "
        else:
            next_pre += "|    "
        for child_index in range(len(self.__children)-1):
            self.__children[child_index].\
                printTreeGivenPrefix(next_pre, False)
        self.__children[-1].printTreeGivenPrefix(next_pre, True)

def __repr__(self):
    return "[" + str(self.__key) + "".join(
        [ repr(child) for child in self.__children ]) + "]"

# This static function will load a tree with the format of below:
# [root[child_1][child_2]...[child_n]]
# Each child_i can be a tree with the above format, too
# pos is the position in the given string
@staticmethod
def loadTree(tree_str, pos = 0):
    new_node = None
    while pos < len(tree_str):
        if tree_str[pos] == "[":
            pos += 1
            new_node = Tree(tree_str[pos])
            while pos < len(tree_str) and tree_str[pos + 1] != "]":
                pos += 1
                child_tree, pos = Tree.loadTree(tree_str, pos)
                if child_tree:
                    new_node.__children.append(child_tree)
                    new_node.__num_of_descendants += \
                        1 + child_tree.__num_of_descendants
            return new_node, pos + 1
        else:
            pos += 1
    return new_node, pos

def find_largest(self):
    if self.__num_of_descendants == 1:
        return self.__children[0]

    else:
        largest_child = self.__children[0]
        for child in self.__children:
            if child.__num_of_descendants > \
               largest_child.__num_of_descendants:
                largest_child = child
            if child.__num_of_descendants == \
               largest_child.__num_of_descendants:
                if child.__key > largest_child.__key:
                    largest_child = child
    return largest_child

def convert_to_binary_tree(self):
    if self.__num_of_descendants != 0:
        if self.__num_of_descendants < 3:
            for child in self.__children:
                child.convert_to_binary_tree()

        if self.__num_of_descendants > 2:
            left_child = self.__children[0]
            for child in self.__children[1:]:
                if len(child.__children) > len(left_child.__children):
                    left_child = child
                elif len(child.__children) == len(left_child.__children):
                    if child.__key > left_child.__key:
                        left_child = child
            self.__children.remove(left_child)
            self.__num_of_descendants -= 1

            right_child = self.__children[0]
            for child in self.__children[1:]:
                if len(child.__children) > len(right_child.__children):
                    right_child = child
                elif len(child.__children) == len(right_child.__children):
                    if child.__key > right_child.__key:
                        right_child = child
            self.__children.remove(right_child)
            self.__num_of_descendants -= 1
            print(self.__num_of_descendants)
            print(self.__children)
            print(left_child)
            print(right_child)

            #Move remaining children two either left_child or right_child.
            while self.__num_of_descendants != 0:
                largest_child = self.find_largest()
                print(largest_child)
                if left_child.__num_of_descendants < \
                   right_child.__num_of_descendants:
                    left_child.__children.append(largest_child)
                    left_child.__num_of_descendants += 1
                    self.__children.remove(largest_child)
                    self.__num_of_descendants -= 1                        

                elif left_child.__num_of_descendants > \
                   right_child.__num_of_descendants:
                    right_child.__children.append(largest_child)
                    right_child.__num_of_descendants += 1
                    self.__children.remove(largest_child)
                    self.__num_of_descendants -= 1                        

                elif left_child.__num_of_descendants == \
                   right_child.__num_of_descendants:
                    if left_child.__key > right_child.__key:
                        left_child.__children.append(largest_child)
                        left_child.__num_of_descendants += 1
                        self.__children.remove(largest_child)
                        self.__num_of_descendants -= 1                            
                    else:
                        right_child.__children.append(largest_child)
                        right_child.__num_of_descendants += 1
                        self.__children.remove(largest_child)
                        self.__num_of_descendants -= 1
            #Now run recursion on left and right binary children.
            self.__children.append(left_child)
            self.__children.append(right_child)
            self.__num_of_descendants = 2
            print(self.__children)
            for child in self.__children:
                child.convert_to_binary_tree()
def main():
    tree, processed_chars = Tree.loadTree('[z[y][x][w][v]]]')
    tree.convert_to_binary_tree()
    tree.printTree()
    print(tree)

if __name__ == "__main__":
    main()

Мне нужно преобразовать данное дерево в двоичное дерево. Если узел в дереве имеет более 2 дочерних элементов, я должен назначить дочерний элемент с наибольшим количеством потомков левым узлом, а дочерний элемент со вторым по величине числом потомков — правым дочерним элементом. Остальные дочерние элементы добавляются следующим образом: 1) Берется дочерний элемент с наибольшим числом потомков. 2) Добавляется в левый/правый узел. Тот, у кого в это время меньше детей.

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

I get a print out like this...
2 #Number of 'z' children after left and right node chosen.
[[w], [v]] #Children of 'z'
[y] #Binary left child of 'z'
[x] #Binary right child of 'z'
[w] #This is a bug. It should be choosing 'v' as larger child of 'z' and assigning it to left child 'y'
[v] #This is a bug. see above.
[[y[w]], [x[v]]] #These are the children of node 'z'
â””--> z #schematic of binary tree
     |--> y
     |    â””--> w
     â””--> x
          â””--> v
[z[y[w]][x[v]]] #final binary tree 

person zachary    schedule 27.03.2014    source источник
comment
Кроме того: здесь нет причин ставить перед вашими переменными префикс __. Если вы настаиваете, вы можете использовать _ как дружеское напоминание о том, что некоторые из них не предназначены для изменения вне класса, но даже это часто излишне.   -  person DSM    schedule 27.03.2014
comment
Да, мне тоже не нравится, как их зовут, но мой инструктор настаивает...   -  person zachary    schedule 27.03.2014
comment
Можете ли вы проверить отступ кода? Являются ли функция main и шаблон if __name__ == "__main__" действительно отступом под class или они на самом деле находятся на верхнем уровне?   -  person Blckknght    schedule 27.03.2014
comment
Ну, а если дочерних узлов больше трех, то эти дочерние узлы приходится переставлять. То есть, если узел имеет трех дочерних элементов, два самых больших значения назначаются как левый и правый дочерние элементы узла, а оставшиеся дочерние элементы назначаются новому левому или правому дочернему элементу. Если узел имеет только двух дочерних элементов, то он уже находится в формате двоичного дерева, и ничего делать не нужно. Но я хочу запустить форматирование для этих двух детей, если у одного из них есть 3+ ребенка.   -  person zachary    schedule 27.03.2014
comment
Извините, отступ отключен. основная функция и если name == main находятся на основном уровне.   -  person zachary    schedule 27.03.2014
comment
Залипание print в нескольких местах предполагает, что он попал в петлю for child in self.__children, в состояние, где left_child is right_child и left_child is self, и, таким образом, self.__children растет и растет. Обычно это признак того, что рекурсия отключается недостаточно рано, но уже за полночь по восточному стандартному времени. :^)   -  person DSM    schedule 27.03.2014


Ответы (1)


Комментарий DSM помог мне понять, что происходит. После выбора left_child и right_child в первых частях метода convert_to_binary_tree вы не удаляете их из списка дочерних элементов. Это означает, что позже, когда вы добавите всех дочерних элементов текущего узла в новых родителей, вы добавите левого и правого дочерних элементов к себе (или друг к другу). Когда вы возвращаетесь к этим дочерним элементам, вы можете зацикливаться бесконечно.

Я не очень понимаю логику вашего выбора left_child и right_child, поэтому у меня нет фиксированного кода, чтобы предложить вам. Быстрым, но некрасивым решением было бы поместить оператор if child in (left_child, right_child): continue в начало цикла for, где вы назначаете других дочерних элементов новым родителям.

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

person Blckknght    schedule 27.03.2014
comment
Эй, спасибо, что нашли время, чтобы посмотреть на код. Я закончил тем, что соскреб большую часть этого, после того, как понял, что вы только что опубликовали. Я обновил свой код, но по какой-то причине я все еще получаю ошибку. Смотрите обновленный пост, если у вас есть время :) - person zachary; 27.03.2014
comment
Я сузил его. По какой-то причине программа вводит определение find_largest(self): if child.__key › наибольший_дочерний.__key: наибольший_дочерний = дочерний Он не регистрирует v › w. - person zachary; 27.03.2014