Сохранение рекурсивных результатов в обратном направлении

#python #class #recursion

#python #класс #рекурсия

Вопрос:

Я работаю над этой проблемой, которую задал Google (не мне):

Учитывая корень двоичного дерева, реализуйте serialize(root), который сериализует дерево в строку, и deserialize(ы), который десериализует строку обратно в дерево.

Это то, что у меня есть до сих пор, но, похоже, я не могу заставить функцию serialize сохранять результаты (снизу дерева и вверх) в строку. Так что я могу распечатать результаты, просто не сохранять их…

 class Tree:

    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None
        self.ser_str = None

    def insert(self, data):
        if self.data:
            if data < self.data:
                if self.left is None:
                    self.left = Tree(data)
                else:
                    self.left.insert(data)
            elif data >= self.data:
                if self.right is None:
                    self.right = Tree(data)
                else:
                    self.right.insert(data)
        else:
            self.data = data

    def serialize(self):
        if self.left:
            self.left.serialize()
        print(self.data)
        if self.right:
            self.right.serialize()

root = Tree(23)
root.insert(10);root.insert(124);root.insert(101);root.insert(1);root.insert(40)
print("here comes the sun")
test = root.serialize()
  

Комментарии:

1. Вы должны «return» вместо «print», и вы должны объединить результаты вложенных вызовов для «сериализации» с данными текущего узла в строку, чтобы вернуть ее.

2. Должны ли вы сами придумать формат для строки или он задан? Я не думаю, что ваш формат допускал бы однозначную десериализацию.

3. @tobias_k: Ваше предположение так же хорошо, как и мое =) Все, что у меня есть, это этот вопрос =) В моей голове я думал о string str() , но я не знаю…

4. @MichaelButscher: звучит фантастически, я просто не могу понять, как это сделать

5. В чем проблема? Как вернуть значение из функции, как присвоить возвращаемое значение переменной (вы делаете это уже в коде), как объединить строки?

Ответ №1:

Как отмечалось в комментариях, проблема с вашим методом заключается в том, что вы просто печатаете данные, но никогда не объединяете их в строку и не возвращаете. Однако этот формат также не допускает однозначной десериализации дерева.

Если вы не хотите использовать библиотеки, подобные json , которые делают это тривиальным, вы можете прибегнуть к легко анализируемому формату, такому как польская нотация, где a b записывается как a b . В случае дерева data соответствует оператору, left а right ветвь and соответствует операндам.

 def serialize(t):
    if t is None:
        return "-"
    else:
        return f"{t.data} {serialize(t.left)} {serialize(t.right)}"
    
def deserialize(s):
    if isinstance(s, str): s = iter(s.split())
    # using an iterator of chunks makes this easier
    d = next(s)
    if d == "-":
        return None
    else:
        return Tree(d, deserialize(s), deserialize(s))
  

(Обратите внимание, что я создал эти функции, а не методы, и добавил несколько необязательных параметров в Tree конструктор, чтобы упростить код.)

При тестировании с вашим деревом это сериализовало дерево в 23 10 1 - - - 124 101 40 - - - - . (Затем я десериализовал дерево и снова сериализовал его и получил тот же формат, поэтому десериализация тоже должна работать.) Вы можете добавить скобки, чтобы лучше видеть древовидную структуру в строке: (23 (10 (1 - -) -) (124 (101 (40 - -) -) -)) , но формат однозначен даже без скобок.

Это в основном соответствует простому обходу дерева в предварительном порядке, тогда как вы выполняете обход по порядку, который, как и инфиксная нотация a b , не является однозначным без круглых скобок. Обход по порядку возвращает элементы в отсортированном порядке, что хорошо для некоторых применений, но не здесь, поскольку это означает, что по-разному структурированные деревья, содержащие один и тот же элемент, будут сериализованы в один и тот же отсортированный список. Вы могли бы, конечно, просто добавить скобки, но это значительно усложнит синтаксический анализ / десериализацию. С помощью скобок и по порядку ваше дерево будет (((- 1 -) 10 -) 23 (((- 40 -) 101 -) 124 -)) .

(Примечание: даже если сериализация неоднозначна, вы можете воссоздать из нее двоичное дерево, но форма этого дерева будет другой; в частности, это будет вырожденное двоичное дерево, если вы просто вставляете элементы в отсортированном порядке, поскольку они выходят из вашего in-order-обход.)

Комментарии:

1. Большое вам спасибо! 🙂 это всегда так просто и элегантно, когда вы видите, что все сделано правильно!

2. какие два аргумента вы добавили в класс дерева? Я предполагаю, что это левый и правый узлы, но тогда мне любопытно, как left не всегда равно right, поскольку аргументы всегда одинаковы =)

3. @Helen Точно, левый и правый узлы, поэтому я могу сделать этот бит в одной строке вместо четырех. Они не совпадают, потому что аргумент deserialize , s , является итератором токенов в сериализации, и состояние этого итератора будет другим после сериализации left дочернего элемента.

4. Ах, конечно! Теперь я понимаю, это блестяще! Еще раз спасибо =)