Python二进制搜索树的实例教程

142 阅读4分钟

树是任何遵循某些特定规则的数据结构。树必须正好有一个根节点。如果你有两个根节点,你实际上有的就是两棵树。一棵树有一个根,一个根就等于一棵树。此外,每个节点可以有任意数量的子节点,也可以有零个子节点,在这种情况下,我们称该节点为叶。一个节点可以有100个或更多的孩子,仍然是一棵有效的树。除了根以外,每个节点都正好链接到一个父节点。如果你有一个有两个父母的孩子,那么就会出现循环,导致各种问题。一个节点不能成为它自己的父节点。如果你的数据结构符合这些标准,那么它就是一棵树。

关于树

有许多不同类型的树,它们都有额外的规则,使它们变得有用。很多时候,树中的每个节点通常都有某种与之相关的数据。想想网络开发中的DOM或操作系统中的目录结构。在我们的案例中,我们将讨论二进制搜索树。在二进制搜索树中,每个节点只有两个子节点,称为左子和右子。这就是为什么它是一棵二进制(二)树。每个节点都有一个与之相关的值。节点左边的值必须小于其父母的值。节点右边的值必须大于其父辈的值。BST的值不可以相等,每个值必须是唯一的。

构建一棵树

下面的代码通过Node类实现了一棵树。这是我们的树的一个基本构建模块。当它被初始化时,我们可以传递一些数据给它。每个节点都会有一个该节点代表的数字。除了Node之外,我们还有一些数据和左、右两个孩子的属性。有了基本结构的实现,我们就开始构建一棵树。我们可以在父节点的左边和右边附加新的节点。所以node.left等于节点15,node.right等于25。请注意,我把数据较小的节点,在这种情况下,15放在左边,我把数据较大的节点,在这种情况下,25放在右边。这是因为我们正在建立一个二进制搜索树,较小的数值总是放在左边,较大的数值放在右边。再扩展一下树,我们设置node.left.left等于12,这将是我们树中最小的数字。Node.left.right被设置为16。节点.right.left被设置为23。Node.right.right被设置为100。然后你可以看到我们如何能够访问树中任何一点的值。

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


class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name


node = Node(20)

node.left = Node(15)
node.right = Node(25)

node.left.left = Node(12)
node.left.right = Node(16)

node.right.left = Node(23)
node.right.right = Node(100)

nice_tree = Tree(node, 'A real nice Tree.')

print(nice_tree.name)
print(nice_tree.root.data)
print(nice_tree.root.left.data)
print(nice_tree.root.right.right.data)
A real nice Tree.
20
15
100

如何搜索一棵树

在这一节中,我们可以为树添加搜索功能,因为我们知道小值总是在左边,大值总是在右边。搜索方法将接受一个目标,也就是我们要在树上寻找的数据。我们要做的第一件事是检查当前节点是否与我们要搜索的目标相匹配。如果self.data等于目标,那么我们就找到了我们要找的东西。通过返回我们在树上找到的节点,我们可以停止搜索。否则,如果没有找到匹配,我们需要继续寻找。我们通过递归检查树的左边和右边来做到这一点。

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

    def search(self, target):
        if self.data == target:
            print("Found the value!")
            return self

        if self.left and self.data > target:
            return self.left.search(target)

        if self.right and self.data < target:
            return self.right.search(target)

        print("Value is not in tree")


class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name

    def search(self, target):
        return self.root.search(target)


node = Node(20)

node.left = Node(15)
node.right = Node(25)

node.left.left = Node(12)
node.left.right = Node(16)

node.right.left = Node(23)
node.right.right = Node(100)

nice_tree = Tree(node, 'A real nice Tree')

result = nice_tree.search(23)
if result:
    print(f'{result.data} is in the "{nice_tree.name}" tree')
Found the value!
23 is in the "A real nice Tree" tree

如何遍历一棵树

三种常见的树形遍历算法包括Traverse In OrderTraverse Pre OrderTraverse Post Order。你可以在所引用的链接中阅读它们工作的具体细节。下面是我们的实现代码。

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

    def search(self, target):
        if self.data == target:
            print("Found it!")
            return self

        if self.left and self.data > target:
            return self.left.search(target)

        if self.right and self.data < target:
            return self.right.search(target)

        print("Value is not in tree")

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

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

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


class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name

    def search(self, target):
        return self.root.search(target)


node = Node(20)
node.left = Node(15)
node.right = Node(25)
node.left.left = Node(12)
node.left.right = Node(16)
node.right.left = Node(23)
node.right.right = Node(100)

node.traversePreorder()
print('-' * 20)
node.traverseInorder()
print('-' * 20)
node.traversePostorder()
20
15
12
16
25
23
100
--------------------
12
15
16
20
23
25
100
--------------------
12
16
15
23
100
25
20

获取最大高度

一棵树的高度是指从根到树的最深处的叶子有多少个节点。一棵树的高度是它的最大高度。高度很重要,因为它决定了搜索一棵树的最大运行时间。要找到一棵树的最大高度,可以用一个小的递归函数来完成,如图所示。

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

    def search(self, target):
        if self.data == target:
            print("Found it!")
            return self

        if self.left and self.data > target:
            return self.left.search(target)

        if self.right and self.data < target:
            return self.right.search(target)

        print("Value is not in tree")

    def height(self, h=0):
        leftHeight = self.left.height(h + 1) if self.left else h
        rightHeight = self.right.height(h + 1) if self.right else h
        return max(leftHeight, rightHeight)


class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name

    def search(self, target):
        return self.root.search(target)

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


node = Node(20)
node.left = Node(15)
node.right = Node(25)
node.left.left = Node(12)
node.left.right = Node(16)
node.right.left = Node(23)
node.right.right = Node(100)

print(node.height())
2

从一个给定的深度获取节点

在这一节中,我们有一个函数,它接收一个任意的深度,如2,并从左到右打印出该深度的节点。如果你想打印出树的全部内容,这是一个有用的东西。

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

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

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

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

    def search(self, target):
        if self.data == target:
            print("Found it!")
            return self

        if self.left and self.data > target:
            return self.left.search(target)

        if self.right and self.data < target:
            return self.right.search(target)

        print("Value is not in tree")

    def getNodesAtDepth(self, depth, nodes=[]):
        if depth == 0:
            nodes.append(self.data)
            return nodes

        if self.left:
            self.left.getNodesAtDepth(depth - 1, nodes)
        else:
            nodes.extend([None] * 2 ** (depth - 1))

        if self.right:
            self.right.getNodesAtDepth(depth - 1, nodes)
        else:
            nodes.extend([None] * 2 ** (depth - 1))
        return nodes

    def height(self, h=0):
        leftHeight = self.left.height(h + 1) if self.left else h
        rightHeight = self.right.height(h + 1) if self.right else h
        return max(leftHeight, rightHeight)


class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name

    def traverseInorder(self):
        self.root.traverseInorder()

    def traversePreorder(self):
        self.root.traversePreorder()

    def traversePostorder(self):
        self.root.traversePostorder()

    def search(self, target):
        return self.root.search(target)

    def getNodesAtDepth(self, depth):
        return self.root.getNodesAtDepth(depth)

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


node = Node(20)
node.left = Node(15)
node.right = Node(25)
node.left.left = Node(12)
node.left.right = Node(16)
node.right.left = Node(23)
node.right.right = Node(100)

print(node.getNodesAtDepth(0))
print(node.getNodesAtDepth(1))
print(node.getNodesAtDepth(2))
[20]
[20, 15, 25]
[20, 15, 25, 12, 16, 23, 100]

打印一棵树

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

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

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

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

    def search(self, target):
        if self.data == target:
            print("Found it!")
            return self

        if self.left and self.data > target:
            return self.left.search(target)

        if self.right and self.data < target:
            return self.right.search(target)

        print("Value is not in tree")

    def getNodesAtDepth(self, depth, nodes=[]):
        if depth == 0:
            nodes.append(self.data)
            return nodes

        if self.left:
            self.left.getNodesAtDepth(depth - 1, nodes)
        else:
            nodes.extend([None] * 2 ** (depth - 1))

        if self.right:
            self.right.getNodesAtDepth(depth - 1, nodes)
        else:
            nodes.extend([None] * 2 ** (depth - 1))
        return nodes

    def height(self, h=0):
        leftHeight = self.left.height(h + 1) if self.left else h
        rightHeight = self.right.height(h + 1) if self.right else h
        return max(leftHeight, rightHeight)


class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name

    def _nodeToChar(self, n, spacing):
        if n is None:
            return '_' + (' ' * spacing)
        spacing = spacing - len(str(n)) + 1
        return str(n) + (' ' * spacing)

    def print(self, label=''):
        print(self.name + ' ' + label)
        height = self.root.height()
        spacing = 3
        width = int((2 ** height - 1) * (spacing + 1) + 1)
        # Root offset
        offset = int((width - 1) / 2)
        for depth in range(0, height + 1):
            if depth > 0:
                # print directional lines
                print(' ' * (offset + 1) + (' ' * (spacing + 2)).join(['/' + (' ' * (spacing - 2)) + '\\'] * (2 ** (depth - 1))))
            row = self.root.getNodesAtDepth(depth, [])
            print((' ' * offset) + ''.join([self._nodeToChar(n, spacing) for n in row]))
            spacing = offset + 1
            offset = int(offset / 2) - 1
        print('')

    def traverseInorder(self):
        self.root.traverseInorder()

    def traversePreorder(self):
        self.root.traversePreorder()

    def traversePostorder(self):
        self.root.traversePostorder()

    def search(self, target):
        return self.root.search(target)

    def getNodesAtDepth(self, depth):
        return self.root.getNodesAtDepth(depth)

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


tree = Tree(Node(20))
tree.root.left = Node(15)
tree.root.right = Node(25)
tree.root.left.left = Node(12)
tree.root.left.right = Node(16)
tree.root.right.left = Node(23)
tree.root.right.right = Node(100)

tree.print()
      20  
   /     \
  15      25      
 / \     / \
12  16  23  100 

Python 二进制搜索树总结

在本教程中,我们了解了二叉树并回顾了这种数据结构的一些有用的应用。二叉树是一种由最多有两个孩子的节点组成的树状数据结构。每个节点可以有一个右边和左边的孩子。位于顶部的节点被称为根节点。没有子节点的节点被称为叶节点。大多数应用都使用不同类型的二进制树,如尝试、二进制搜索树和B-树。在计算机科学中,二叉树经常被用于搜索和排序,因为它们提供了一种分层存储数据的方法。可以在二叉树上使用的常见操作包括插入、删除和遍历。