Вывод бинарного дерева при использовании обхода по порядку и в прямом порядке

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right
 
def inorderTraversal(root):
 
    if root is None:
        return
 
    inorderTraversal(root.left)
    print(root.data, end=' ')
    inorderTraversal(root.right)
 
def preorderTraversal(root):
 
    if root is None:
        return
 
    print(root.data, end=' ')
    preorderTraversal(root.left)
    preorderTraversal(root.right)
 
def construct(start, end, preorder, pIndex, dict):
 
    # base case
    if start > end:
        return None, pIndex
 
    root = Node(preorder[pIndex])
    pIndex = pIndex + 1
 
    index = dict[root.data]
 
    root.left, pIndex = construct(start, index - 1, preorder, pIndex, dict)
 
    root.right, pIndex = construct(index + 1, end, preorder, pIndex, dict)
 
    return root, pIndex     

def constructTree(inorder, preorder):
 
    dict = {}
    for i, e in enumerate(inorder):
        dict[e] = i
 
    pIndex = 0
 
    return construct(0, len(inorder) - 1, preorder, pIndex, dict)[0]
 
 
if __name__ == '__main__':
 
 
    inorder = [4, 2, 1, 7, 5, 8, 3, 6]
    preorder = [1, 2, 4, 3, 5, 7, 8, 6]
 
    root = constructTree(inorder, preorder)
 
    print("The inorder traversal is ", end='')
    inorderTraversal(root)
 
    preorderTraversal(root)

У меня есть этот код, который строит двоичное дерево, но никак не может отобразить дерево в терминале. Это трудно сделать! Есть ли здесь кто-нибудь, кто мог бы добавить метод, который может отображать двоичное дерево в терминале?

Для приведенного выше примера это может выглядеть так

Двоичное дерево

введите здесь описание изображения


person Robert    schedule 12.04.2021    source источник
comment
Существует библиотека под названием binarytree, которая будет генерировать случайные деревья, или вы можете предоставить свои собственные. Он также сгенерирует его визуально в терминале, pypi.org/project/binarytree.   -  person Lateralus    schedule 12.04.2021
comment
Да, это довольно тяжело. Возможно, вы захотите рассмотреть возможность сделать это нерекурсивно. Вы можете создать стек. Для каждого узла вы добавляете его правый и левый потомки в стек. Тогда в какой-то момент все элементы стека будут принадлежать одному и тому же «уровню» (по высоте). Вы выталкиваете и печатаете их все, пока стек не станет пустым (тогда вы знаете, что уровень был напечатан). Сначала вы можете заставить его просто печатать каждый уровень на новой строке без каких-либо причудливых косых черт. Косая черта и пробелы/отступы - более сложная часть   -  person parsecer    schedule 12.04.2021
comment
Ознакомьтесь с этим geeksforgeeks.org/print-level-order-traversal- строчка-линия. Если вам действительно не нужны косые черты, может быть, просто оставьте это как есть   -  person parsecer    schedule 12.04.2021
comment
Похоже, это может быть домашним заданием. Перейдите по этой ссылке для получения дополнительной информации о двоичных деревьях medium.com/@ajinkyajawale/   -  person smitty_werbenjagermanjensen    schedule 12.04.2021


Ответы (1)


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

В моем алгоритме и ширина, и высота каждого поддерева, и длина ребер являются адаптивными.

Те, кто знает русский язык, могут прочитать мой другой пост на ту же тему, построение бинарного дерева в консоли. В другом посте реализовано несколько алгоритмов визуализации на C++. Если вы не знаете русского языка, вы можете, по крайней мере, скопировать код C++ оттуда.

Попробуйте онлайн!

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right
        
def print_tree(node):
    def inner(node):
        if node is None:
            return []
        sdat = str(node.data)
        l, r = inner(node.left), inner(node.right)
        cl, cr = len((l or ('',))[0]), len((r or ('',))[0])
        s = max(cl, cr)
        sll, slr = (s - cl + 1) // 2, (s - cl) // 2
        srl, srr = (s - cr) // 2, (s - cr + 1) // 2
        v = [' ' * s + sdat + ' ' * s]
        v.extend([' ' * (s - i - 1) + '/' + ' ' * i + ' ' * len(sdat) +
            ' ' * i + '\\' + ' ' * (s - i - 1) for i in range(s // 2)])
        v.extend([(' ' * sll + l[i] + ' ' * slr if i < len(l) else ' ' * s) +
            ' ' * len(sdat) + (' ' * srl + r[i] + ' ' * srr if i < len(r) else ' ' * s)
            for i in range(max(len(l), len(r)))])
        return v
    print('\n'.join(inner(node)))
        
if __name__ == '__main__':
    root = Node(1, Node(2, Node(4)), Node(3, Node(5, Node(7), Node(8)), Node(6)))
    print_tree(root)

Выход:

       1
      / \
     /   \
    /     \
   2       3
  4       / \
         5   6
        7 8

Первый алгоритм выше был выполнен с использованием предварительного заказа. Я предоставляю 2-й алгоритм, используя порядок. Он имеет другой более простой вывод:

Попробуйте онлайн!

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right
        
def print_tree(node):
    def inner(node, *, upref = '', cpref = '', dpref = ''):
        if node is None:
            return
        inner(node.right, upref = dpref + '  |',
            cpref = dpref + '  /', dpref = dpref + '   ')
        print(cpref + '--' + str(node.data))
        inner(node.left, upref = upref + '   ',
            cpref = upref + '  \\', dpref = upref + '  |')
    inner(node)
        
if __name__ == '__main__':
    root = Node(1, Node(2, Node(4)), Node(3, Node(5, Node(7), Node(8)), Node(6)))
    print_tree(root)

Выход:

     /--6
  /--3
  |  |  /--8
  |  \--5
  |     \--7
--1
  \--2
     \--4

Реализация 3-го алгоритма, который выполняет предварительный заказ, но он намного проще, чем 1-й алгоритм:

Try it онлайн!

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right
        
def print_tree(node):
    def inner(node, *, last = True, pref = ''):
        if node is None:
            return
        print(pref + ('\\-' if last else '|-') + str(node.data))
        inner(node.right, last = False, pref = pref + ('  ' if last else '| '))
        inner(node.left, last = True, pref = pref + ('  ' if last else '| '))
    inner(node)
        
if __name__ == '__main__':
    root = Node(1, Node(2, Node(4)), Node(3, Node(5, Node(7), Node(8)), Node(6)))
    print_tree(root)

Выход:

\-1
  |-3
  | |-6
  | \-5
  |   |-8
  |   \-7
  \-2
    \-4
person Arty    schedule 12.04.2021
comment
Трудно ли выполнять одну и ту же работу для заказов на заказ и после заказа? Можете ли вы изменить вопрос, чтобы он работал в двух других случаях? Вы предпочитаете, чтобы я начал другой вопрос? - person Robert; 12.04.2021
comment
@Robert Выводить на консоль можно только одним способом. Никаких других способов. При выводе на консоль нет предзаказа/послезаказа/незаказа. Только дерево обработки в вашем коде может использовать inorder/preorder. Но консольный вывод — это просто вспомогательная функция, она не связана со способом обработки данных внутри дерева. Другими словами, вывод в консоль можно сделать для чего угодно - под заказ/предзаказ/после заказа. - person Arty; 12.04.2021
comment
stackoverflow.com/questions/67062345/adjust-constructtree Я думаю, что этот вопрос для вас . Я знаю, что могу создать такое дерево, используя, например, обход в порядке и обратном порядке. Я не думаю, что мы должны изменить print_tree(), а скорее изменить constructTree() - person Robert; 12.04.2021
comment
@ Роберт, я понял, чего ты хочешь. Только что обновил мой ответ, добавил 2-ю часть со 2-м алгоритмом. 1-й алгоритм использует предварительный порядок, 2-й использует порядок. Пожалуйста, положите посмотреть. - person Arty; 12.04.2021
comment
Это не совсем то, чего я хочу. То, что вы сделали, печатает дерево после его создания. Допустим, он еще не построен. Предположим, у меня есть inorder=[3, 7, 1, 10, 9, 5, 8, 6, 4, 2] и postorder = [3, 7, 10, 9, 1, 8, 4, 6, 2, 5]. Как бы вы напечатали дерево, используя информацию. constructTree() сделал бы это, но он не адаптирован для обратного порядка. Поэтому мне нужна версия этой функции, которая работала бы как с порядком-после заказа, так и с порядком-предзаказом. - person Robert; 12.04.2021
comment
@Robert Я только что обновил свой ответ третьим алгоритмом предварительного заказа вывода на консоль. И только что увидел твой комментарий. Мне нужно подумать о том, что вы на самом деле имеете в виду. Я думаю, что если вам предоставлены данные предварительного или неупорядоченного порядка, вам нужно сначала рекурсивно построить свое дерево, а затем использовать мои функции для вывода на консоль. Таким образом, функция construct() должна поддерживать данные внутреннего/предварительного заказа, а не функция вывода на консоль. Функция вывода на консоль должна принимать только уже построенное дерево. Если вы хотите, чтобы я реализовал функцию construct(), то скажите, это не составит труда. - person Arty; 12.04.2021
comment
Две другие функции печати были не нужны, но приятно иметь их. Я хотел бы, чтобы вы адаптировали код из моего вопроса, чтобы он также мог строить дерево для случаев inorder-postorder и preorder-postorder. Пока мы рассматриваем только случай предварительного заказа, а не два других. Вы можете добавить свой ответ здесь: stackoverflow.com/questions/67062345/adjust-constructtree - person Robert; 12.04.2021