바이너리 트리를 구현하는 방법은 무엇입니까?


104

파이썬에서 이진 트리를 구현하는 데 사용할 수있는 최고의 데이터 구조는 무엇입니까?


2
여기에 많은 솔루션이 BST를 구현하고 있지만 Biner Tree 구현에 의문이 제기되었습니다
vikas mehta

질문 제목에 Python의 트리 알고리즘을 원한다고 지정할 수 있습니까?
Ken Tran

답변:


97

다음은 이진 검색 트리의 간단한 재귀 구현입니다.

#!/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 is None:
            self.root = Node(val)
        else:
            self._add(val, self.root)

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

    def find(self, val):
        if self.root is not 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 is not None):
            self._find(val, node.l)
        elif (val > node.v and node.r is not 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 is not None:
            self._printTree(self.root)

    def _printTree(self, node):
        if node is not 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()

19
멋진 구현입니다. 나는 단지 몇 가지 스타일 을 지적하기 위해 여기에 있다 . 파이썬은 일반적으로 않는 node is not None대신의 (node!=None). 또한 __str__printTree 메서드 대신 함수를 사용할 수 있습니다 .
Jeff Mandell

2
또한 _find는 다음과 같아야합니다. def _find(self, val, node): if(val == node.v): return node elif(val < node.v and node.l != None): return self._find(val, node.l) elif(val > node.v and node.r != None): return self._find(val, node.r)
darkhipo 2015

4
이진 검색 트리가 어디 left<=root<=right입니까?
Sagar Shah

3
tree.find (0), tree.find (2), tree.find (4), tree.find (8) 모두 None을 반환합니다.
토니 왕

3
작은 버그가 있습니다. 기존 키를 삽입하려고 할 때 트리 아래로 내려가 중복 키가있는 새 노드를 만듭니다.
Diego Gallegos

27
# 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)

여기에 대해 자세히 읽어보십시오 :- 이것은 바이너리 트리의 매우 간단한 구현 입니다.

이것은 중간에 질문이있는 멋진 튜토리얼입니다.


2
코드 insertLeft가 깨져서 이진 트리의

2
tree.left = self.left self.left = 트리 : 그것은 쉽게 다음 줄을 swaping 해결할 수 있습니다
AirelleJab

1
마지막 링크가 끊어졌습니다. 고칠 수 있습니까.
Arjee

13

[인터뷰에 필요한 것] 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을 자식으로 갖는 트리의 루트입니다.

여기에 이미지 설명 입력


이것은 다른 많은 답변에서 이미 설명 된 것 이상을 추가합니까?
Sneftel

4
@Sneftel 다른 답변은 이진 트리에 대해 지나치게 정교합니다. 이것은 바이너리 트리 구현에 필요한 필수 부분입니다. 다른 답변으로 인해 새로운 사람들이 이해하기가 너무 어려워서 새로운 사람들을 돕기 위해 최소한의 정보 만 게시 할 것이라고 생각했습니다. 다른 답변 중 일부는 기사 및 저널 논문에 적합합니다.) 이것은 또한 누군가가 소프트웨어 인터뷰에 필요한 부분입니다.
apadana

3
그것은 가치있는 단순성을 추가합니다.
pylang

2
간단하고 매우 논리적입니다. 큰. 나는 그것을 좋아했다!
Apostolos

11

Python에서 BST의 간단한 구현

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)

2
세미콜론으로 일부 문장을 끝내고 세미콜론없이 일부 문장을 끝냈습니다. 그 이유를 설명해 주시겠습니까? 추신-저는 Python 초보자이므로 기본적인 질문을하는 것입니다.
outlier229

@ outlier229 위 코드의 모든 세미콜론은 선택 사항이며 제거해도 아무것도 변경되지 않습니다. 내 생각 엔 Fox는 줄 끝에 세미콜론이 필요한 C ++ 또는 Java와 같은 언어를 코딩하는 데 단순히 사용된다는 것입니다. 선택적 사용 외에도 세미콜론을 사용하여 명령문을 한 줄로 연결할 수 있습니다. 예 : a = 1; b = 2; c = 3은 파이썬에서 유효한 한 줄입니다.
physicsGuy

8

목록을 사용하여 이진 트리를 구현하는 매우 빠르고 더러운 방법입니다. 가장 효율적이지 않으며 nil 값을 너무 잘 처리하지도 않습니다. 그러나 그것은 (적어도 나에게는) 매우 투명합니다.

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'])

아주 좋아요! 나는 도울 수 없었지만 결과 트리는 왼쪽 하위 트리의 모든 요소가 v보다 작다는 불변성을 유지하지 않는다고 언급했습니다.-이진 검색 트리에 중요한 속성입니다. (예, OP가 "검색 트리"를 요청하지 않았다는 것을 알고 있습니다) 그러나 FWIW는 _add ()의 확인을 간단하게 수정하여 수행 할 수 있습니다. 그런 다음 inorder traversal은 정렬 된 목록을 제공합니다.
thayne

6

나는 도울 수 없지만 여기에서 대부분의 답변이 이진 검색 트리를 구현하고 있음을 알 수 있습니다. 이진 검색 트리! = 이진 트리.

  • 이진 검색 트리에는 매우 특정한 속성이 있습니다. 모든 노드 X의 경우 X의 키는 왼쪽 자식의 하위 키보다 크고 오른쪽 자식의 하위 키보다 작습니다.

  • 바이너리 트리는 그러한 제한을 부과하지 않습니다. 이진 트리는 '키'요소와 '왼쪽'과 '오른쪽'이라는 두 개의 하위 요소가있는 데이터 구조입니다.

  • 트리는 각 노드가 임의의 수의 자식을 가질 수있는 바이너리 트리의 훨씬 더 일반적인 경우입니다. 일반적으로 각 노드에는 목록 / 배열 유형의 '하위'요소가 있습니다.

이제 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

4

두 개의 수업이 필요하지 않습니다

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()

7
두 개의 클래스가있는 것이 좋습니다. 즉 더 나은 구현

1
@ user3022012 귀하의 의견은 단순히 잘못되었습니다. 정의에 따라 트리는 데이터와 하위 트리 (이진 트리의 경우 두 개의 하위 트리)로 구성되며 트리이기도합니다. 이유는 없습니다. 루트 노드를 다르게 트리화할 이유가 없습니다.
guyarad dec

1
원래 포스터 만 요청 이진 트리 구현이 아닌 바이너리 검색 ... 나무
guyarad

2

좀 더 "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)

2
#!/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=' ')

선주문 순회가 잘못되었습니다. 각 지점을 개별적으로 테스트해야합니다.
Svante

나는 주문과 주문 후의 경우에만 각 지점을 별도로 테스트해야한다고 생각합니다. 내가 쓴 사전 주문 방법은 올바른 결과를 제공합니다. 이 방법이 어떤 경우에 중단되는지 말해 줄 수 있습니까? 나는 이후 주문과에 주문했던 그러나, 내가 별도로 두 가지를 테스트하자
정강이

그랬던 것처럼 왼쪽 아이가 None이라면 오른쪽 아이도 보지 않을 것입니다.
Svante

이진 트리의 왼쪽 자식이 없음이면 오른쪽 자식도 없음이라고 가정 할 수 있습니다. 노드가 2 개와 2 개 노드로만 분기되고 왼쪽이 없음이면 오른쪽도 없음이됩니다.
eshanrh

2

Node연결된 노드 의 기반 클래스는 표준 접근 방식입니다. 시각화하기 어려울 수 있습니다.

Python Patterns-Implementing Graphs 에 대한 에세이 에서 동기를 얻은 간단한 사전을 고려하십시오.

주어진

이진 트리

               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],
}

세부

  • 각 키-값 쌍은 자식을 가리키는 고유 한 노드 입니다.
  • 목록 (또는 튜플)에는 순서가 지정된 왼쪽 / 오른쪽 자식 쌍이 있습니다.
  • A의 딕셔너리는 삽입을 지시하는 데 하여 첫 번째 항목이 루트라고 가정합니다.
  • 일반적인 메서드는 dict를 변경하거나 순회하는 함수일 수 있습니다 (참조 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']

이것은 노드와 자식의 사전에 적용되는 폭 우선 검색 (수준 순서) 알고리즘 입니다.

  1. FIFO 대기열을 초기화합니다 . 우리는 deque, but a queue또는 a list작품을 사용합니다 (후자는 비효율적입니다).
  2. 루트 노드를 가져 와서 대기열에 넣습니다 (루트가 dict의 첫 번째 항목 인 Python 3.6 이상이라고 가정).
  3. 반복적으로 노드를 대기열에서 빼고 하위 항목을 대기열에 넣고 노드 값을 산출합니다.

나무에 대한 이 심층 자습서 를 참조하십시오 .


통찰력

일반적으로 순회에 대한 좋은 점은 대기열을 스택 으로 간단히 교체하여 깊이 우선 검색 (DFS) 에 대한 후자의 반복적 접근 방식을 쉽게 변경할 수 있습니다. (LIFO 대기열이라고도 함 . 이것은 단순히 우리가 대기열에 넣는 것과 같은 쪽에서 대기열을 빼는 것을 의미합니다. DFS를 사용하면 각 지점을 검색 할 수 있습니다.

어떻게? 를 사용하고 있으므로 (오른쪽) deque으로 변경 node = q.popleft()하여 스택을 에뮬레이트 할 수 있습니다 node = q.pop(). 그 결과 오른쪽으로 선호되는 선주문 된 DFS : ['a', 'c', 'f', 'b', 'e', 'd'].


1
import random

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

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

    def length(self):
        return self.size

    def inorder(self, node):
        if node == None:
            return None
        else:
            self.inorder(node.left)
            print node.key,
            self.inorder(node.right)

    def search(self, k):
        node = self.root
        while node != None:
            if node.key == k:
                return node
            if node.key > k:
                node = node.left
            else:
                node = node.right
        return None

    def minimum(self, node):
        x = None
        while node.left != None:
            x = node.left
            node = node.left
        return x

    def maximum(self, node):
        x = None
        while node.right != None:
            x = node.right
            node = node.right
        return x

    def successor(self, node):
        parent = None
        if node.right != None:
            return self.minimum(node.right)
        parent = node.p
        while parent != None and node == parent.right:
            node = parent
            parent = parent.p
        return parent

    def predecessor(self, node):
        parent = None
        if node.left != None:
            return self.maximum(node.left)
        parent = node.p
        while parent != None and node == parent.left:
            node = parent
            parent = parent.p
        return parent

    def insert(self, k):
        t = TreeNode(k)
        parent = None
        node = self.root
        while node != None:
            parent = node
            if node.key > t.key:
                node = node.left
            else:
                node = node.right
        t.p = parent
        if parent == None:
            self.root = t
        elif t.key < parent.key:
            parent.left = t
        else:
            parent.right = t
        return t


    def delete(self, node):
        if node.left == None:
            self.transplant(node, node.right)
        elif node.right == None:
            self.transplant(node, node.left)
        else:
            succ = self.minimum(node.right)
            if succ.p != node:
                self.transplant(succ, succ.right)
                succ.right = node.right
                succ.right.p = succ
            self.transplant(node, succ)
            succ.left = node.left
            succ.left.p = succ

    def transplant(self, node, newnode):
        if node.p == None:
            self.root = newnode
        elif node == node.p.left:
            node.p.left = newnode
        else:
            node.p.right = newnode
        if newnode != None:
            newnode.p = node.p

이것을 실행 한 후, 새로운 노드 z, y, x, w, u, v가 때때로 할당 될 수 있고, 때때로 다음과 같은 버그가있을 것입니다 : print u.key AttributeError : 'NoneType'object has no attribute 'key'I wonder how 덕분에 그것을 해결하기
water0

1

이 구현은 트리 구조를 파괴하지 않고 삽입, 찾기 및 삭제 작업을 지원합니다. 이것은 밴 랜스 트리가 아닙니다.

# Class for construct the nodes of the tree. (Subtrees)
class Node:
def __init__(self, key, parent_node = None):
    self.left = None
    self.right = None
    self.key = key
    if parent_node == None:
        self.parent = self
    else:
        self.parent = parent_node

# Class with the  structure of the tree. 
# This Tree is not balanced.
class Tree:
def __init__(self):
    self.root = None

# Insert a single element
def insert(self, x):
    if(self.root == None):
        self.root = Node(x)
    else:
        self._insert(x, self.root)

def _insert(self, x, node):
    if(x < node.key):
        if(node.left == None):
            node.left = Node(x, node)
        else:
            self._insert(x, node.left)
    else:
        if(node.right == None):
            node.right = Node(x, node)
        else:
            self._insert(x, node.right)

# Given a element, return a node in the tree with key x. 
def find(self, x):
    if(self.root == None):
        return None
    else:
        return self._find(x, self.root)
def _find(self, x, node):
    if(x == node.key):
        return node
    elif(x < node.key):
        if(node.left == None):
            return None
        else:
            return self._find(x, node.left)
    elif(x > node.key):
        if(node.right == None):
            return None
        else:
            return self._find(x, node.right)

# Given a node, return the node in the tree with the next largest element.
def next(self, node):
    if node.right != None:
        return self._left_descendant(node.right)
    else:
        return self._right_ancestor(node)

def _left_descendant(self, node):
    if node.left == None:
        return node
    else:
        return self._left_descendant(node.left)

def _right_ancestor(self, node):
    if node.key <= node.parent.key:
        return node.parent
    else:
        return self._right_ancestor(node.parent)

# Delete an element of the tree
def delete(self, x):
    node = self.find(x)
    if node == None:
        print(x, "isn't in the tree")
    else:
        if node.right == None:
            if node.left == None:
                if node.key < node.parent.key:
                    node.parent.left = None
                    del node # Clean garbage
                else:
                    node.parent.right = None
                    del Node # Clean garbage
            else:
                node.key = node.left.key
                node.left = None
        else:
            x = self.next(node)
            node.key = x.key
            x = None


# tests
t = Tree()
t.insert(5)
t.insert(8)
t.insert(3)
t.insert(4)
t.insert(6)
t.insert(2)

t.delete(8)
t.delete(5)

t.insert(9)
t.insert(1)

t.delete(2)
t.delete(100)

# Remember: Find method return the node object. 
# To return a number use t.find(nº).key
# But it will cause an error if the number is not in the tree.
print(t.find(5)) 
print(t.find(8))
print(t.find(4))
print(t.find(6))
print(t.find(9))

1

이미 많은 좋은 솔루션이 게시 된 것을 알고 있지만 일반적으로 바이너리 트리에 대해 다른 접근 방식을 사용합니다. 일부 Node 클래스를 사용하여 직접 구현하는 것이 더 읽기 쉽지만 노드가 많으면 메모리에 대해 매우 탐욕스러워 질 수 있습니다. 하나의 복잡성 계층을 추가하고 노드를 파이썬 목록에 저장 한 다음 목록 만 사용하여 트리 동작을 시뮬레이션하는 것이 좋습니다.

필요할 때 마지막으로 트리의 노드를 나타내도록 Node 클래스를 정의 할 수 있지만 목록에서 [value, left, right] 간단한 형식으로 유지하면 메모리의 절반 이하를 사용합니다!

다음은 노드를 배열에 저장하는 이진 검색 트리 클래스의 빠른 예입니다. 그것은 추가, 제거, 찾기와 같은 기본 fonctions를 제공합니다 ...

"""
Basic Binary Search Tree class without recursion...
"""

__author__ = "@fbparis"

class Node(object):
    __slots__ = "value", "parent", "left", "right"
    def __init__(self, value, parent=None, left=None, right=None):
        self.value = value
        self.parent = parent
        self.left = left
        self.right = right

    def __repr__(self):
        return "<%s object at %s: parent=%s, left=%s, right=%s, value=%s>" % (self.__class__.__name__, hex(id(self)), self.parent, self.left, self.right, self.value)

class BinarySearchTree(object):
    __slots__ = "_tree"
    def __init__(self, *args):
        self._tree = []
        if args:
            for x in args[0]:
                self.add(x)

    def __len__(self):
        return len(self._tree)

    def __repr__(self):
        return "<%s object at %s with %d nodes>" % (self.__class__.__name__, hex(id(self)), len(self))

    def __str__(self, nodes=None, level=0):
        ret = ""
        if nodes is None:
            if len(self):
                nodes = [0]
            else:
                nodes = []
        for node in nodes:
            if node is None:
                continue
            ret += "-" * level + " %s\n" % self._tree[node][0]
            ret += self.__str__(self._tree[node][2:4], level + 1)
        if level == 0:
            ret = ret.strip()
        return ret

    def __contains__(self, value):
        if len(self):
            node_index = 0
            while self._tree[node_index][0] != value:
                if value < self._tree[node_index][0]:
                    node_index = self._tree[node_index][2]
                else:
                    node_index = self._tree[node_index][3]
                if node_index is None:
                    return False
            return True
        return False

    def __eq__(self, other):
        return self._tree == other._tree

    def add(self, value):
        if len(self):
            node_index = 0
            while self._tree[node_index][0] != value:
                if value < self._tree[node_index][0]:
                    b = self._tree[node_index][2]
                    k = 2
                else:
                    b = self._tree[node_index][3]
                    k = 3
                if b is None:
                    self._tree[node_index][k] = len(self)
                    self._tree.append([value, node_index, None, None])
                    break
                node_index = b
        else:
            self._tree.append([value, None, None, None])

    def remove(self, value):
        if len(self):
            node_index = 0
            while self._tree[node_index][0] != value:
                if value < self._tree[node_index][0]:
                    node_index = self._tree[node_index][2]
                else:
                    node_index = self._tree[node_index][3]
                if node_index is None:
                    raise KeyError
            if self._tree[node_index][2] is not None:
                b, d = 2, 3
            elif self._tree[node_index][3] is not None:
                b, d = 3, 2
            else:
                i = node_index
                b = None
            if b is not None:
                i = self._tree[node_index][b]
                while self._tree[i][d] is not None:
                    i = self._tree[i][d]
                p = self._tree[i][1]
                b = self._tree[i][b]
                if p == node_index:
                    self._tree[p][5-d] = b
                else:
                    self._tree[p][d] = b
                if b is not None:
                    self._tree[b][1] = p
                self._tree[node_index][0] = self._tree[i][0]
            else:
                p = self._tree[i][1]
                if p is not None:
                    if self._tree[p][2] == i:
                        self._tree[p][2] = None
                    else:
                        self._tree[p][3] = None
            last = self._tree.pop()
            n = len(self)
            if i < n:
                self._tree[i] = last[:]
                if last[2] is not None:
                    self._tree[last[2]][1] = i
                if last[3] is not None:
                    self._tree[last[3]][1] = i
                if self._tree[last[1]][2] == n:
                    self._tree[last[1]][2] = i
                else:
                    self._tree[last[1]][3] = i
        else:
            raise KeyError

    def find(self, value):
        if len(self):
            node_index = 0
            while self._tree[node_index][0] != value:
                if value < self._tree[node_index][0]:
                    node_index = self._tree[node_index][2]
                else:
                    node_index = self._tree[node_index][3]
                if node_index is None:
                    return None
            return Node(*self._tree[node_index])
        return None

모든 노드를 제거하고 BST 구조를 유지할 수 있도록 부모 속성을 추가했습니다.

가독성, 특히 "제거"기능에 대해 죄송합니다. 기본적으로 노드가 제거되면 트리 배열을 팝하고 마지막 요소로 대체합니다 (마지막 노드를 제거하려는 경우 제외). BST 구조를 유지하기 위해 제거 된 노드는 왼쪽 자식의 최대 값 또는 오른쪽 자식의 최소값으로 대체되고 인덱스를 유효하게 유지하기 위해 몇 가지 작업을 수행해야하지만 충분히 빠릅니다.

나는 내부 기수 트라이를 사용하여 큰 단어 사전을 구축하기 위해이 기술을 더 발전된 것들에 사용했고 메모리 소비를 7-8로 나눌 수있었습니다 (여기에서 예제를 볼 수 있습니다 : https://gist.github.com/fbparis). / b3ddd5673b603b42c880974b23db7cda )


0

여기 에서 가져온 이진 검색 트리 의 좋은 구현 :

'''
A binary search Tree
'''
from __future__ import print_function
class Node:

    def __init__(self, label, parent):
        self.label = label
        self.left = None
        self.right = None
        #Added in order to delete a node easier
        self.parent = parent

    def getLabel(self):
        return self.label

    def setLabel(self, label):
        self.label = label

    def getLeft(self):
        return self.left

    def setLeft(self, left):
        self.left = left

    def getRight(self):
        return self.right

    def setRight(self, right):
        self.right = right

    def getParent(self):
        return self.parent

    def setParent(self, parent):
        self.parent = parent

class BinarySearchTree:

    def __init__(self):
        self.root = None

    def insert(self, label):
        # Create a new Node
        new_node = Node(label, None)
        # If Tree is empty
        if self.empty():
            self.root = new_node
        else:
            #If Tree is not empty
            curr_node = self.root
            #While we don't get to a leaf
            while curr_node is not None:
                #We keep reference of the parent node
                parent_node = curr_node
                #If node label is less than current node
                if new_node.getLabel() < curr_node.getLabel():
                #We go left
                    curr_node = curr_node.getLeft()
                else:
                    #Else we go right
                    curr_node = curr_node.getRight()
            #We insert the new node in a leaf
            if new_node.getLabel() < parent_node.getLabel():
                parent_node.setLeft(new_node)
            else:
                parent_node.setRight(new_node)
            #Set parent to the new node
            new_node.setParent(parent_node)      

    def delete(self, label):
        if (not self.empty()):
            #Look for the node with that label
            node = self.getNode(label)
            #If the node exists
            if(node is not None):
                #If it has no children
                if(node.getLeft() is None and node.getRight() is None):
                    self.__reassignNodes(node, None)
                    node = None
                #Has only right children
                elif(node.getLeft() is None and node.getRight() is not None):
                    self.__reassignNodes(node, node.getRight())
                #Has only left children
                elif(node.getLeft() is not None and node.getRight() is None):
                    self.__reassignNodes(node, node.getLeft())
                #Has two children
                else:
                    #Gets the max value of the left branch
                    tmpNode = self.getMax(node.getLeft())
                    #Deletes the tmpNode
                    self.delete(tmpNode.getLabel())
                    #Assigns the value to the node to delete and keesp tree structure
                    node.setLabel(tmpNode.getLabel())

    def getNode(self, label):
        curr_node = None
        #If the tree is not empty
        if(not self.empty()):
            #Get tree root
            curr_node = self.getRoot()
            #While we don't find the node we look for
            #I am using lazy evaluation here to avoid NoneType Attribute error
            while curr_node is not None and curr_node.getLabel() is not label:
                #If node label is less than current node
                if label < curr_node.getLabel():
                    #We go left
                    curr_node = curr_node.getLeft()
                else:
                    #Else we go right
                    curr_node = curr_node.getRight()
        return curr_node

    def getMax(self, root = None):
        if(root is not None):
            curr_node = root
        else:
            #We go deep on the right branch
            curr_node = self.getRoot()
        if(not self.empty()):
            while(curr_node.getRight() is not None):
                curr_node = curr_node.getRight()
        return curr_node

    def getMin(self, root = None):
        if(root is not None):
            curr_node = root
        else:
            #We go deep on the left branch
            curr_node = self.getRoot()
        if(not self.empty()):
            curr_node = self.getRoot()
            while(curr_node.getLeft() is not None):
                curr_node = curr_node.getLeft()
        return curr_node

    def empty(self):
        if self.root is None:
            return True
        return False

    def __InOrderTraversal(self, curr_node):
        nodeList = []
        if curr_node is not None:
            nodeList.insert(0, curr_node)
            nodeList = nodeList + self.__InOrderTraversal(curr_node.getLeft())
            nodeList = nodeList + self.__InOrderTraversal(curr_node.getRight())
        return nodeList

    def getRoot(self):
        return self.root

    def __isRightChildren(self, node):
        if(node == node.getParent().getRight()):
            return True
        return False

    def __reassignNodes(self, node, newChildren):
        if(newChildren is not None):
            newChildren.setParent(node.getParent())
        if(node.getParent() is not None):
            #If it is the Right Children
            if(self.__isRightChildren(node)):
                node.getParent().setRight(newChildren)
            else:
                #Else it is the left children
                node.getParent().setLeft(newChildren)

    #This function traversal the tree. By default it returns an
    #In order traversal list. You can pass a function to traversal
    #The tree as needed by client code
    def traversalTree(self, traversalFunction = None, root = None):
        if(traversalFunction is None):
            #Returns a list of nodes in preOrder by default
            return self.__InOrderTraversal(self.root)
        else:
            #Returns a list of nodes in the order that the users wants to
            return traversalFunction(self.root)

    #Returns an string of all the nodes labels in the list 
    #In Order Traversal
    def __str__(self):
        list = self.__InOrderTraversal(self.root)
        str = ""
        for x in list:
            str = str + " " + x.getLabel().__str__()
        return str

def InPreOrder(curr_node):
    nodeList = []
    if curr_node is not None:
        nodeList = nodeList + InPreOrder(curr_node.getLeft())
        nodeList.insert(0, curr_node.getLabel())
        nodeList = nodeList + InPreOrder(curr_node.getRight())
    return nodeList

def testBinarySearchTree():
    r'''
    Example
                  8
                 / \
                3   10
               / \    \
              1   6    14
                 / \   /
                4   7 13 
    '''

    r'''
    Example After Deletion
                  7
                 / \
                1   4

    '''
    t = BinarySearchTree()
    t.insert(8)
    t.insert(3)
    t.insert(6)
    t.insert(1)
    t.insert(10)
    t.insert(14)
    t.insert(13)
    t.insert(4)
    t.insert(7)

    #Prints all the elements of the list in order traversal
    print(t.__str__())

    if(t.getNode(6) is not None):
        print("The label 6 exists")
    else:
        print("The label 6 doesn't exist")

    if(t.getNode(-1) is not None):
        print("The label -1 exists")
    else:
        print("The label -1 doesn't exist")

    if(not t.empty()):
        print(("Max Value: ", t.getMax().getLabel()))
        print(("Min Value: ", t.getMin().getLabel()))

    t.delete(13)
    t.delete(10)
    t.delete(8)
    t.delete(3)
    t.delete(6)
    t.delete(14)

    #Gets all the elements of the tree In pre order
    #And it prints them
    list = t.traversalTree(InPreOrder, t.root)
    for x in list:
        print(x)

if __name__ == "__main__":
    testBinarySearchTree()

0

상당한 수의 노드가있을 때 더 유용한 @apadana 방법의 변형을 보여주고 싶습니다.

'''
Suppose we have the following tree
      10
    /    \
  11      9
 /  \     / \
7   12  15   8
'''
# Step 1 - Create nodes - Use a list instead of defining each node separately
nlist = [10,11,7,9,15,8,12]; n = []
for i in range(len(nlist)): n.append(Node(nlist[i]))

# Step 2 - Set each node position
n[0].left  = n[1]
n[1].left = n[2]
n[0].right = n[3]
n[3].left = n[4]
n[3].right = n[5]
n[1].right = n[6]

0
class Node:
    """
    single Node for tree
    """

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


class binaryTree:
    """
    binary tree implementation
    """

    def __init__(self):
        self.root = None

    def push(self, element, node=None):
        if node is None:
            node = self.root

        if self.root is None:
            self.root = Node(element)

        else:
            if element < node.data:
                if node.left is not None:
                    self.push(element, node.left)
                else:
                    node.left = Node(element)
            else:
                if node.right is not None:
                    self.push(element, node.right)
                else:
                    node.right = Node(element)

    def __str__(self):
        self.printInorder(self.root)
        return "\n"

    def printInorder(self, node):
        """
        print tree in inorder
        """
        if node is not None:
            self.printInorder(node.left)
            print(node.data)
            self.printInorder(node.right)


def main():
    """
    Main code and logic comes here
    """
    tree = binaryTree()
    tree.push(5)
    tree.push(3)
    tree.push(1)
    tree.push(3)
    tree.push(0)
    tree.push(2)
    tree.push(9)
    tree.push(10)
    print(tree)


if __name__ == "__main__":
    main()

-1

파이썬의 이진 트리

 class Tree(object):
    def __init__(self):
        self.data=None
        self.left=None
        self.right=None
    def insert(self, x, root):
        if root==None:
            t=node(x)
            t.data=x
            t.right=None
            t.left=None
            root=t
            return root
        elif x<root.data:
            root.left=self.insert(x, root.left)
        else:
            root.right=self.insert(x, root.right)
        return root

    def printTree(self, t):
        if t==None:
            return

        self.printTree(t.left)
        print t.data
        self.printTree(t.right)

class node(object):
    def __init__(self, x):
        self.x=x

bt=Tree()
root=None
n=int(raw_input())
a=[]
for i in range(n):
    a.append(int(raw_input()))
for i in range(n):
    root=bt.insert(a[i], root)
bt.printTree(root)

-1

다음은 순회가 아래 코드에서 사용 된 순서대로 트리를 표시하는 재귀 적 접근 방식을 사용하여 이진 트리를 구축하는 데 사용할 수있는 간단한 솔루션입니다.

class Node(object):

    def __init__(self):
        self.left = None
        self.right = None
        self.value = None
    @property
    def get_value(self):
        return self.value

    @property
    def get_left(self):
        return self.left

    @property
    def get_right(self):
        return self.right

    @get_left.setter
    def set_left(self, left_node):
        self.left = left_node
    @get_value.setter
    def set_value(self, value):
        self.value = value
    @get_right.setter
    def set_right(self, right_node):
        self.right = right_node



    def create_tree(self):
        _node = Node() #creating new node.
        _x = input("Enter the node data(-1 for null)")
        if(_x == str(-1)): #for defining no child.
            return None
        _node.set_value = _x #setting the value of the node.
        print("Enter the left child of {}".format(_x))
        _node.set_left = self.create_tree() #setting the left subtree
        print("Enter the right child of {}".format(_x))
        _node.set_right = self.create_tree() #setting the right subtree.

        return _node

    def pre_order(self, root):
        if root is not None:
            print(root.get_value)
            self.pre_order(root.get_left)
            self.pre_order(root.get_right)

if __name__ == '__main__':
    node = Node()
    root_node = node.create_tree()
    node.pre_order(root_node)

가져온 코드 : Python의 이진 트리

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.