Làm thế nào để thực hiện một cây nhị phân?


104

Cấu trúc dữ liệu nào tốt nhất có thể được sử dụng để triển khai cây nhị phân trong Python?


2
Rất nhiều giải pháp ở đây đang triển khai BST nhưng được đặt câu hỏi là Thực hiện Biner Tree
vikas mehta, 14/05

Có thể chỉ định rằng bạn muốn thuật toán cây bằng Python trong tiêu đề của câu hỏi?
Ken Tran

Câu trả lời:


97

Đây là cách triển khai đệ quy đơn giản của tôi về cây tìm kiếm nhị phân.

#!/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
Thực hiện tốt. Tôi chỉ ở đây để chỉ ra một số nội dung phong cách . python thường làm node is not Nonethay vì của bạn (node!=None). Ngoài ra, bạn có thể sử dụng __str__hàm thay vì phương thức printTree.
Jeff Mandell

2
Ngoài ra, _find của bạn có thể phải là: 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,

4
Đây không phải là cây tìm kiếm nhị phân ở đâu left<=root<=right?
Sagar Shah

3
tree.find (0), tree.find (2), tree.find (4), tree.find (8) đều trả về Không có.
Tony Wang

3
Có một lỗi nhỏ, khi bạn cố gắng chèn một khóa hiện có thì nó sẽ đi xuống cây để tạo một nút mới với khóa trùng lặp.
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)

Đọc thêm về nó Tại đây: -Đây là một thực hiện rất đơn giản của cây nhị phân.

Đây là một hướng dẫn hay với các câu hỏi ở giữa


2
Các mã trong insertLeftđược chia và sẽ tạo ra một vòng lặp vô hạn trên bất kỳ nỗ lực để traverse đệ quy xuống chi nhánh tận cùng bên trái cây nhị phân
talonmies

2
Nó có thể được dễ dàng cố định bởi swap những dòng sau: tree.left = self.left self.left = cây
AirelleJab

1
liên kết cuối cùng bị hỏng. Bạn có thể sửa nó không.
Arjee

13

[Những gì bạn cần cho các cuộc phỏng vấn] Một lớp Node là cấu trúc dữ liệu đủ để biểu diễn một cây nhị phân.

(Trong khi các câu trả lời khác hầu hết đều đúng, chúng không bắt buộc đối với cây nhị phân: không cần mở rộng lớp đối tượng, không cần phải là BST, không cần nhập deque).

class Node:

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

Đây là một ví dụ về cây:

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

Trong ví dụ này, n1 là gốc của cây có n2, n3 là con của nó.

nhập mô tả hình ảnh ở đây


Điều này có thêm bất cứ điều gì ngoài những gì đã được mô tả trong nhiều câu trả lời khác không?
Sneftel

4
@Sneftel Các câu trả lời khác quá phức tạp đối với cây nhị phân. Đây là phần bắt buộc cần thiết để triển khai cây nhị phân. Những câu trả lời khác khiến người mới khó hiểu nên tôi nghĩ chỉ đăng mức tối thiểu để giúp những người mới hơn. Một số câu trả lời khác rất tốt cho các bài báo và bài báo;) Đây cũng là phần mà ai đó cần cho các cuộc phỏng vấn phần mềm.
apadana

3
Nó làm tăng thêm tính đơn giản, đó là giá trị.
pylang

2
Đơn giản và rất logic. Tuyệt quá. Tôi yêu nó!
Apostolos

11

Triển khai đơn giản BST bằng 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)

2
Bạn đã kết thúc một số câu bằng dấu chấm phẩy và một số câu không có dấu chấm phẩy. Bạn có thể vui lòng giải thích lý do cho điều đó? Tái bút - Tôi là người mới bắt đầu sử dụng Python, đó là lý do tại sao lại hỏi một câu hỏi cơ bản như vậy.
outlier229

@ outlier229 Tất cả các dấu chấm phẩy trong đoạn mã trên là tùy chọn, việc xóa chúng không thay đổi bất cứ điều gì. Tôi đoán rằng Fox chỉ đơn giản được sử dụng để mã hóa một ngôn ngữ như C ++ hoặc Java, những ngôn ngữ này yêu cầu dấu chấm phẩy ở cuối dòng. Ngoài việc sử dụng tùy chọn đó, dấu chấm phẩy có thể được sử dụng để xâu chuỗi các câu lệnh trong một dòng. Ví dụ a = 1; b = 2; c = 3 sẽ là một dòng đơn hợp lệ trong python.
vật lý:

8

Một cách rất nhanh để triển khai cây nhị phân bằng cách sử dụng danh sách. Không phải là hiệu quả nhất, cũng như không xử lý các giá trị nil quá tốt. Nhưng nó rất minh bạch (ít nhất là đối với tôi):

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

Tạo cây từ một cây có thể lặp lại:

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

Đi ngang qua một cái cây:

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

Rất đẹp! Tôi không thể không nhận xét rằng cây kết quả không giữ bất biến mà tất cả các phần tử trong cây con bên trái đều nhỏ hơn v. - Một thuộc tính quan trọng đối với cây tìm kiếm nhị phân. (Có, tôi nhận ra rằng OP không yêu cầu "cây tìm kiếm") tuy nhiên, FWIW nó có thể được thực hiện với một sửa đổi đơn giản đối với kiểm tra trong _add (). Sau đó, trình duyệt inorder của bạn đưa ra một danh sách được sắp xếp.
thayne

6

Tôi không thể không nhận thấy rằng hầu hết các câu trả lời ở đây đang triển khai Cây tìm kiếm nhị phân. Binary Search Tree! = Cây nhị phân.

  • Cây tìm kiếm nhị phân có một thuộc tính rất cụ thể: đối với bất kỳ nút X nào, khóa của X lớn hơn khóa của bất kỳ con nào của nút con bên trái của nó và nhỏ hơn khóa của bất kỳ nút nào con phải của nó.

  • Cây nhị phân không áp đặt hạn chế như vậy. Cây nhị phân chỉ đơn giản là một cấu trúc dữ liệu với một phần tử 'khóa' và hai phần tử con, chẳng hạn như 'trái' và 'phải'.

  • Cây là một trường hợp tổng quát hơn của Cây nhị phân trong đó mỗi nút có thể có một số nút con tùy ý. Thông thường, mỗi nút có một phần tử 'con' có kiểu danh sách / mảng.

Bây giờ, để trả lời câu hỏi của OP, tôi đang bao gồm việc triển khai đầy đủ Cây nhị phân trong Python. Cấu trúc dữ liệu cơ bản lưu trữ mỗi BinaryTreeNode là một từ điển, do nó cung cấp các tra cứu O (1) tối ưu. Tôi cũng đã triển khai các đường dẫn theo chiều sâu và chiều rộng đầu tiên. Đây là những hoạt động rất phổ biến được thực hiện trên cây.

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

bạn không cần phải có hai lớp học

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
Tốt hơn là nên có hai lớp. Đó là tốt hơn thực hiện

1
@ user3022012 nhận xét của bạn đơn giản là sai. theo định nghĩa, một cây bao gồm dữ liệu và cây con (đối với cây nhị phân, đó là hai cây con), cũng là cây. Không có lý do gì, bất cứ điều gì để tạo cây nút gốc khác nhau.
guyarad

1
người đăng ban đầu chỉ yêu cầu triển khai cây nhị phân chứ không phải cây tìm kiếm nhị phân ...
guyarad

2

Thêm một chút "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=' ')

Việc truyền tải đơn đặt hàng trước bị sai: bạn cần phải kiểm tra từng nhánh riêng biệt.
Svante

Tôi nghĩ, bạn chỉ cần kiểm tra từng chi nhánh riêng biệt trong trường hợp đặt hàng và gửi bài. phương pháp đặt hàng trước tôi đã viết, cho kết quả đúng. Bạn có thể cho tôi biết phương pháp này sẽ phá vỡ trong trường hợp nào không? Tuy nhiên, hãy để tôi kiểm tra tất cả các chi nhánh riêng biệt như tôi đã làm cho hậu tự, trong trật tự
Shanks

Theo cách của nó, nếu đứa trẻ bên trái là Không, nó thậm chí sẽ không nhìn vào đứa trẻ bên phải.
Svante

Ý tôi là, nếu con bên trái của cây nhị phân là không có, chúng ta có thể giả định rằng con bên phải cũng không phải. Nếu một nút phân nhánh thành 2 và chỉ 2 nút và nút bên trái là Không có, nút bên phải cũng sẽ là Không có.
eshanrh

2

Một Nodelớp dựa trên các nút được kết nối là một cách tiếp cận tiêu chuẩn. Những điều này có thể khó hình dung.

Được thúc đẩy từ một bài luận về Mẫu Python - Thực hiện Đồ thị , hãy xem xét một từ điển đơn giản:

Được

Cây nhị phân

               a
              / \
             b   c
            / \   \
           d   e   f

Tạo từ điển các nút duy nhất :

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

Chi tiết

  • Mỗi cặp khóa-giá trị là một nút duy nhất trỏ đến các nút con của nó.
  • Một danh sách (hoặc tuple) chứa một cặp con trái / phải có thứ tự.
  • Với một có thứ tự chèn , giả sử mục nhập đầu tiên là gốc.
  • Các phương thức phổ biến có thể là các hàm thay đổi hoặc duyệt qua dict (xem find_all_paths() ).

Các hàm dựa trên cây thường bao gồm các hoạt động phổ biến sau:

  • đi qua : nhường mỗi nút theo một thứ tự nhất định (thường từ trái sang phải)
    • tìm kiếm theo chiều rộng-đầu tiên (BFS): các cấp độ đi ngang
    • tìm kiếm theo chiều sâu (DFS): duyệt qua các nhánh trước (thứ tự trước / trong / sau)
  • chèn : thêm một nút vào cây tùy thuộc vào số lượng nút con
  • tẩy : loại bỏ một nút tùy thuộc vào số lượng nút con
  • cập nhật : hợp nhất các nút bị thiếu từ cây này sang cây khác
  • lượt truy cập : mang lại giá trị của một nút được duyệt

Hãy thử thực hiện tất cả các thao tác này. Ở đây chúng tôi chứng minh một trong những chức năng này - một trình duyệt BFS:

Thí dụ

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

Đây là thuật toán tìm kiếm theo chiều rộng (thứ tự cấp độ) được áp dụng cho một lệnh của các nút và con.

  1. Khởi tạo hàng đợi FIFO . Chúng tôi sử dụng a deque, nhưng a queuehoặc a listhoạt động (cái sau không hiệu quả).
  2. Lấy và xếp hàng nút gốc (giả sử gốc là mục nhập đầu tiên trong dict, Python 3.6+).
  3. Lặp lại xếp hàng một nút, xếp hàng con của nó và mang lại giá trị nút.

Xem thêm hướng dẫn chuyên sâu về cây này.


Insight

Điều tuyệt vời về đường truyền nói chung, chúng ta có thể dễ dàng thay đổi cách tiếp cận lặp đi lặp lại sau này đối với tìm kiếm theo chiều sâu (DFS) bằng cách chỉ cần thay thế hàng đợi bằng một ngăn xếp (hay còn gọi là Hàng đợi LIFO). Điều này đơn giản có nghĩa là chúng tôi xếp hàng từ cùng một phía mà chúng tôi xếp hàng. DFS cho phép chúng tôi tìm kiếm từng nhánh.

Làm sao? Vì chúng ta đang sử dụng a deque, chúng ta có thể mô phỏng một ngăn xếp bằng cách thay đổi node = q.popleft()thành node = q.pop()(phải). Kết quả là một quyền tối huệ, DFS đặt hàng trước : ['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

Sau khi chạy nó, các nút mới z, y, x, w, u, v đôi khi có thể được gán, đôi khi có lỗi, như sau: print u.key AttributeError: Đối tượng 'NoneType' không có thuộc tính 'key' Tôi tự hỏi làm thế nào để khắc phục nó, cảm ơn
water0

1

Việc triển khai này hỗ trợ các thao tác chèn, tìm và xóa mà không phá hủy cấu trúc của cây. Đây không phải là cây banlanced.

# 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

Tôi biết nhiều giải pháp tốt đã được đăng nhưng tôi thường có một cách tiếp cận khác cho cây nhị phân: sử dụng một số lớp Node và triển khai trực tiếp nó dễ đọc hơn nhưng khi bạn có nhiều nút, nó có thể trở nên rất tham lam về bộ nhớ, vì vậy tôi đề xuất thêm một lớp phức tạp và lưu trữ các nút trong danh sách python, sau đó mô phỏng hành vi cây chỉ sử dụng danh sách.

Bạn vẫn có thể định nghĩa một lớp Node để cuối cùng đại diện cho các nút trong cây khi cần thiết, nhưng việc giữ chúng ở dạng đơn giản [giá trị, trái, phải] trong danh sách sẽ sử dụng một nửa bộ nhớ hoặc ít hơn!

Đây là một ví dụ nhanh về lớp Cây tìm kiếm nhị phân lưu trữ các nút trong một mảng. Nó cung cấp các fonctions cơ bản như thêm, bớt, tìm ...

"""
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

Tôi đã thêm thuộc tính cha để bạn có thể xóa bất kỳ nút nào và duy trì cấu trúc BST.

Xin lỗi vì tính dễ đọc, đặc biệt là đối với chức năng "loại bỏ". Về cơ bản, khi một nút bị xóa, chúng ta bật mảng cây và thay thế nó bằng phần tử cuối cùng (trừ trường hợp chúng ta muốn loại bỏ nút cuối cùng). Để duy trì cấu trúc BST, nút bị loại bỏ được thay thế bằng nút tối đa của nút con bên trái hoặc nút nhỏ nhất của nút con bên phải và một số thao tác phải được thực hiện để giữ các chỉ mục hợp lệ nhưng nó đủ nhanh.

Tôi đã sử dụng kỹ thuật này cho những thứ nâng cao hơn để xây dựng một số từ điển từ lớn với bộ ba cơ số bên trong và tôi có thể chia mức tiêu thụ bộ nhớ cho 7-8 (bạn có thể xem ví dụ ở đây: https://gist.github.com/fbparis / b3ddd5673b603b42c880974b23db7cda )


0

Cách triển khai tốt cây tìm kiếm nhị phân , được lấy từ đây :

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

Tôi muốn hiển thị một biến thể của phương thức @ apadana, sẽ hữu ích hơn khi có một số lượng đáng kể các nút:

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

Cây nhị phân trong Python

 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

Đây là một giải pháp đơn giản có thể được sử dụng để xây dựng cây nhị phân bằng cách sử dụng phương pháp đệ quy để hiển thị cây theo thứ tự duyệt đã được sử dụng trong đoạn mã dưới đây.

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)

Mã lấy từ: Cây nhị phân trong Python

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.