Как реализовать двоичное дерево? - PullRequest
85 голосов
/ 08 апреля 2010

Какая структура данных лучше всего подходит для реализации двоичного дерева в Python?

Ответы [ 16 ]

75 голосов
/ 04 марта 2015

Вот моя простая рекурсивная реализация двоичного дерева.

#!/usr/bin/python

class Node:
    def __init__(self, val):
        self.l = None
        self.r = None
        self.v = val

class Tree:
    def __init__(self):
        self.root = None

    def getRoot(self):
        return self.root

    def add(self, val):
        if(self.root == None):
            self.root = Node(val)
        else:
            self._add(val, self.root)

    def _add(self, val, node):
        if(val < node.v):
            if(node.l != None):
                self._add(val, node.l)
            else:
                node.l = Node(val)
        else:
            if(node.r != None):
                self._add(val, node.r)
            else:
                node.r = Node(val)

    def find(self, val):
        if(self.root != None):
            return self._find(val, self.root)
        else:
            return None

    def _find(self, val, node):
        if(val == node.v):
            return node
        elif(val < node.v and node.l != None):
            self._find(val, node.l)
        elif(val > node.v and node.r != None):
            self._find(val, node.r)

    def deleteTree(self):
        # garbage collector will do this for us. 
        self.root = None

    def printTree(self):
        if(self.root != None):
            self._printTree(self.root)

    def _printTree(self, node):
        if(node != None):
            self._printTree(node.l)
            print str(node.v) + ' '
            self._printTree(node.r)

#     3
# 0     4
#   2      8
tree = Tree()
tree.add(3)
tree.add(4)
tree.add(0)
tree.add(8)
tree.add(2)
tree.printTree()
print (tree.find(3)).v
print tree.find(10)
tree.deleteTree()
tree.printTree()
26 голосов
/ 16 сентября 2014
# simple binary tree
# in this implementation, a node is inserted between an existing node and the root


class BinaryTree():

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

    def getLeftChild(self):
        return self.left
    def getRightChild(self):
        return self.right
    def setNodeValue(self,value):
        self.rootid = value
    def getNodeValue(self):
        return self.rootid

    def insertRight(self,newNode):
        if self.right == None:
            self.right = BinaryTree(newNode)
        else:
            tree = BinaryTree(newNode)
            tree.right = self.right
            self.right = tree

    def insertLeft(self,newNode):
        if self.left == None:
            self.left = BinaryTree(newNode)
        else:
            tree = BinaryTree(newNode)
            tree.left = self.left
            self.left = tree


def printTree(tree):
        if tree != None:
            printTree(tree.getLeftChild())
            print(tree.getNodeValue())
            printTree(tree.getRightChild())



# test tree

def testTree():
    myTree = BinaryTree("Maud")
    myTree.insertLeft("Bob")
    myTree.insertRight("Tony")
    myTree.insertRight("Steven")
    printTree(myTree)

Подробнее об этом здесь: -Это очень простая реализация двоичного дерева.

Это хороший учебник с вопросами между

8 голосов
/ 20 мая 2016

Простая реализация BST в Python

class TreeNode:
    def __init__(self, value):
        self.left = None
        self.right = None
        self.data = value

class Tree:
    def __init__(self):
        self.root = None

    def addNode(self, node, value):
        if(node==None):
            self.root = TreeNode(value)
        else:
            if(value<node.data):
                if(node.left==None):
                    node.left = TreeNode(value)
                else:
                    self.addNode(node.left, value)
            else:
                if(node.right==None):
                    node.right = TreeNode(value)
                else:
                    self.addNode(node.right, value)

    def printInorder(self, node):
        if(node!=None):
            self.printInorder(node.left)
            print(node.data)
            self.printInorder(node.right)

def main():
    testTree = Tree()
    testTree.addNode(testTree.root, 200)
    testTree.addNode(testTree.root, 300)
    testTree.addNode(testTree.root, 100)
    testTree.addNode(testTree.root, 30)
    testTree.printInorder(testTree.root)
7 голосов
/ 16 ноября 2014

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

def _add(node, v):
    new = [v, [], []]
    if node:
        left, right = node[1:]
        if not left:
            left.extend(new)
        elif not right:
            right.extend(new)
        else:
            _add(left, v)
    else:
        node.extend(new)

def binary_tree(s):
    root = []
    for e in s:
        _add(root, e)
    return root

def traverse(n, order):
    if n:
        v = n[0]
        if order == 'pre':
            yield v
        for left in traverse(n[1], order):
            yield left
        if order == 'in':
            yield v
        for right in traverse(n[2], order):
            yield right
        if order == 'post':
            yield v

Построение дерева из итерируемого:

 >>> tree = binary_tree('A B C D E'.split())
 >>> print tree
 ['A', ['B', ['D', [], []], ['E', [], []]], ['C', [], []]]

Обход дерева:

 >>> list(traverse(tree, 'pre')), list(traverse(tree, 'in')), list(traverse(tree, 'post'))
 (['A', 'B', 'D', 'E', 'C'],
  ['D', 'B', 'E', 'A', 'C'],
  ['D', 'E', 'B', 'C', 'A'])
4 голосов
/ 18 августа 2018

Не могу не заметить, что большинство ответов здесь реализуют дерево двоичного поиска.Двоичное дерево поиска! = Двоичное дерево.

  • Двоичное дерево поиска имеет очень специфическое свойство: для любого узла X ключ X больше, чем ключ любого потомка его левого потомка,и меньше, чем ключ любого потомка своего правого потомка.

  • Двоичное дерево не налагает такого ограничения.Двоичное дерево - это просто структура данных с ключевым элементом и двумя дочерними элементами, скажем, «влево» и «вправо».

  • Дерево - это еще более общий случайДвоичное дерево, где каждый узел может иметь произвольное количество дочерних элементов.Как правило, каждый узел имеет элемент 'children', который имеет тип list / array.

Теперь, чтобы ответить на вопрос OP, я включаю полную реализацию двоичного дерева в Python,Базовая структура данных, хранящая каждый BinaryTreeNode, представляет собой словарь, учитывая, что он предлагает оптимальный O (1) поиск.Я также реализовал обход в глубину и в ширину.Это очень распространенные операции, выполняемые на деревьях.

from collections import deque

class BinaryTreeNode:
    def __init__(self, key, left=None, right=None):
        self.key = key
        self.left = left
        self.right = right

    def __repr__(self):
        return "%s l: (%s) r: (%s)" % (self.key, self.left, self.right)

    def __eq__(self, other):
        if self.key == other.key and \
            self.right == other.right and \
                self.left == other.left:
            return True
        else:
            return False

class BinaryTree:
    def __init__(self, root_key=None):
        # maps from BinaryTreeNode key to BinaryTreeNode instance.
        # Thus, BinaryTreeNode keys must be unique.
        self.nodes = {}
        if root_key is not None:
            # create a root BinaryTreeNode
            self.root = BinaryTreeNode(root_key)
            self.nodes[root_key] = self.root

    def add(self, key, left_key=None, right_key=None):
        if key not in self.nodes:
            # BinaryTreeNode with given key does not exist, create it
            self.nodes[key] = BinaryTreeNode(key)
        # invariant: self.nodes[key] exists

        # handle left child
        if left_key is None:
            self.nodes[key].left = None
        else:
            if left_key not in self.nodes:
                self.nodes[left_key] = BinaryTreeNode(left_key)
            # invariant: self.nodes[left_key] exists
            self.nodes[key].left = self.nodes[left_key]

        # handle right child
        if right_key == None:
            self.nodes[key].right = None
        else:
            if right_key not in self.nodes:
                self.nodes[right_key] = BinaryTreeNode(right_key)
            # invariant: self.nodes[right_key] exists
            self.nodes[key].right = self.nodes[right_key]

    def remove(self, key):
        if key not in self.nodes:
            raise ValueError('%s not in tree' % key)
        # remove key from the list of nodes
        del self.nodes[key]
        # if node removed is left/right child, update parent node
        for k in self.nodes:
            if self.nodes[k].left and self.nodes[k].left.key == key:
                self.nodes[k].left = None
            if self.nodes[k].right and self.nodes[k].right.key == key:
                self.nodes[k].right = None
        return True

    def _height(self, node):
        if node is None:
            return 0
        else:
            return 1 + max(self._height(node.left), self._height(node.right))

    def height(self):
        return self._height(self.root)

    def size(self):
        return len(self.nodes)

    def __repr__(self):
        return str(self.traverse_inorder(self.root))

    def bfs(self, node):
        if not node or node not in self.nodes:
            return
        reachable = []    
        q = deque()
        # add starting node to queue
        q.append(node)
        while len(q):
            visit = q.popleft()
            # add currently visited BinaryTreeNode to list
            reachable.append(visit)
            # add left/right children as needed
            if visit.left:
                q.append(visit.left)
            if visit.right:
                q.append(visit.right)
        return reachable

    # visit left child, root, then right child
    def traverse_inorder(self, node, reachable=None):
        if not node or node.key not in self.nodes:
            return
        if reachable is None:
            reachable = []
        self.traverse_inorder(node.left, reachable)
        reachable.append(node.key)
        self.traverse_inorder(node.right, reachable)
        return reachable

    # visit left and right children, then root
    # root of tree is always last to be visited
    def traverse_postorder(self, node, reachable=None):
        if not node or node.key not in self.nodes:
            return
        if reachable is None:
            reachable = []
        self.traverse_postorder(node.left, reachable)
        self.traverse_postorder(node.right, reachable)
        reachable.append(node.key)
        return reachable

    # visit root, left, then right children
    # root is always visited first
    def traverse_preorder(self, node, reachable=None):
        if not node or node.key not in self.nodes:
            return
        if reachable is None:
            reachable = []
        reachable.append(node.key)
        self.traverse_preorder(node.left, reachable)
        self.traverse_preorder(node.right, reachable)
        return reachable
3 голосов
/ 23 января 2016

вам не нужно иметь два класса

class Tree:
    val = None
    left = None
    right = None

    def __init__(self, val):
        self.val = val


    def insert(self, val):
        if self.val is not None:
            if val < self.val:
                if self.left is not None:
                    self.left.insert(val)
                else:
                    self.left = Tree(val)
            elif val > self.val:
                if self.right is not None:
                    self.right.insert(val)
                else:
                    self.right = Tree(val)
            else:
                return
        else:
            self.val = val
            print("new node added")

    def showTree(self):
        if self.left is not None:
            self.left.showTree()
        print(self.val, end = ' ')
        if self.right is not None:
            self.right.showTree()
2 голосов
/ 02 сентября 2018

[Что вам нужно для интервью] Класс Node - это достаточный минимум для представления двоичного дерева.

(Хотя другие ответы в основном правильные, они не требуются для двоичного дерева, нет необходимости расширять класс объекта, нет необходимости быть BST, нет необходимости импортировать deque).

class Node:

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

Вот пример дерева:

n1 = Node(1)
n2 = Node(2)
n3 = Node(3)
n1.left  = n2
n1.right = n3

В этом примере n1 является корнем дерева, чьи дочерние элементы n2, n3.

enter image description here

2 голосов
/ 01 октября 2017
#!/usr/bin/python

class BinaryTree:
    def __init__(self, left, right, data):
        self.left = left
        self.right = right
        self.data = data


    def pre_order_traversal(root):
        print(root.data, end=' ')

        if root.left != None:
            pre_order_traversal(root.left)

        if root.right != None:
            pre_order_traversal(root.right)

    def in_order_traversal(root):
        if root.left != None:
            in_order_traversal(root.left)
        print(root.data, end=' ')
        if root.right != None:
            in_order_traversal(root.right)

    def post_order_traversal(root):
        if root.left != None:
            post_order_traversal(root.left)
        if root.right != None:
            post_order_traversal(root.right)
        print(root.data, end=' ')
2 голосов
/ 10 июня 2017

Еще немного "Pythonic"?

class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

    def __repr__(self):
        return str(self.value)



class BST:
    def __init__(self):
        self.root = None

    def __repr__(self):
        self.sorted = []
        self.get_inorder(self.root)
        return str(self.sorted)

    def get_inorder(self, node):
        if node:
            self.get_inorder(node.left)
            self.sorted.append(str(node.value))
            self.get_inorder(node.right)

    def add(self, value):
        if not self.root:
            self.root = Node(value)
        else:
            self._add(self.root, value)

    def _add(self, node, value):
        if value <= node.value:
            if node.left:
                self._add(node.left, value)
            else:
                node.left = Node(value)
        else:
            if node.right:
                self._add(node.right, value)
            else:
                node.right = Node(value)



from random import randint

bst = BST()

for i in range(100):
    bst.add(randint(1, 1000))
print (bst)
1 голос
/ 10 мая 2019
Класс подключенных узлов на основе

A Node является стандартным подходом.Это может быть трудно визуализировать.

По мотивам эссе на Шаблоны Python - Реализация графиков , рассмотрим простой словарь:

Дано

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

               a
              / \
             b   c
            / \   \
           d   e   f

Код

Создать словарь из уникальных узлов:

tree = {
   "a": ["b", "c"],
   "b": ["d", "e"],
   "c": [None, "f"],
   "d": [None, None],
   "e": [None, None],
   "f": [None, None],
}

Подробности

  • Каждая пара ключ-значение представляет собой уникальный узел , указывающий на его дочерние элементы.
  • Список (или кортеж) содержит упорядоченную пару левых / правых дочерних элементов.
  • С диктовкой , имеющей упорядоченную вставку , предположим, что первая запись является корнем.
  • Обычными методами могут быть функции, которые изменяют или пересекают диктовку (см. find_all_paths()).

Древовидные функции часто включают следующие общие операции:

  • traverse : вывод каждого узла в заданном порядке (обычно слева насправа)
    • поиск в ширину (BFS): уровни перемещения
    • поиск в глубину (DFS): сначала переходы по ветвям (до / в / после заказа)
  • insert : добавить узел в дерево в зависимости от количества дочерних элементов
  • remove : удалить узел в зависимости от количествадети
  • обновление : объединить отсутствующие узлы из одного дерева в другое
  • посещение : получить значение пройденного узла

Попробуйте реализовать все эти операции.Здесь мы демонстрируем одну этих функций - обход BFS:

Пример

import collections as ct


def traverse(tree):
    """Yield nodes from a tree via BFS."""
    q = ct.deque()                                         # 1
    root = next(iter(tree))                                # 2
    q.append(root)

    while q:
        node = q.popleft()
        children = filter(None, tree.get(node))
        for n in children:                                 # 3 
            q.append(n)
        yield node

list(traverse(tree))
# ['a', 'b', 'c', 'd', 'e', 'f']

This алгоритм поиска в ширину * (порядок уровней) , применяемый к узлу узлов и дочерних элементов.

  1. Инициализировать очередь FIFO .Мы используем deque, но queue или list работает (последний неэффективен).
  2. Получить и поставить в очередь корневой узел (предполагает, что корень является первой записью в dict, Python 3.6 +).
  3. Итеративно удаляет очередь из узла, ставит в очередь его дочерние элементы и выдает значение узла.

См. также этотглубина учебник на деревьях.


Insight

Что-то замечательное в обходах вообще, мы можем легко изменить последний итерационный подход к поиск в глубину (DFS) , просто заменив очередь на стек (он же LIFO Queue).Это просто означает, что мы снимаем с той же стороны, что ставим в очередь.DFS позволяет нам искать каждую ветвь.

Как?Поскольку мы используем deque, мы можем эмулировать стек, изменив node = q.popleft() на node = q.pop() (справа).В результате получается правый, предзаказ DFS : ['a', 'c', 'f', 'b', 'e', 'd'].

...