Python implements the delete function of the binary search tree

Python implements the delete function of the binary search tree

Binary Search Tree (Binary Search Tree) is also called sorted binary tree and ordered binary tree.

The implementation of binary search tree can refer to: https://blog.csdn.net/weixin_43790276/article/details/105753543

This article uses Python to implement the delete function of the binary search tree. Before that, you must first know the characteristics of the binary search tree:

1. If the left subtree of the binary tree is not empty, the values ​​of all nodes on the left subtree are less than the value of its root node.

2. If the right subtree of the binary tree is not empty, the values ​​of all nodes on the right subtree are greater than the value of its root node.

3. If you look at it independently, the left and right subtrees are also binary search trees.

One, prepare the binary search tree class

Before implementing the delete function of the binary search tree, first implement a class SearchBinaryTree of the binary search tree.

# coding=utf-8
class Node(object):
    """节点类"""
    def __init__(self, data, left_child=None, right_child=None):
        self.data = data
        self.parent = None
        self.left_child = left_child
        self.right_child = right_child


class SearchBinaryTree(object):
    """二叉树类"""
    def __init__(self):
        self.__root = None
        self.prefix_branch = '├'
        self.prefix_trunk = '|'
        self.prefix_leaf = '└'
        self.prefix_empty = ''
        self.prefix_left = '─L─'
        self.prefix_right = '─R─'

    def is_empty(self):
        return not self.__root

    @property
    def root(self):
        return self.__root

    @root.setter
    def root(self, value):
        self.__root = value if isinstance(value, Node) else Node(value)

    def show_tree(self):
        if self.is_empty():
            print('空二叉树')
            return
        print('-' * 20)
        print(self.__root.data)
        self.__print_tree(self.__root)
        print('-' * 20)

    def insert(self, root, value):
        """二叉搜索树插入节点-递归"""
        node = value if isinstance(value, Node) else Node(value)
        if self.is_empty():
            self.root = node
            return
        if root is None:
            root = node
        elif node.data < root.data:
            root.left_child = self.insert(root.left_child, value)
            root.left_child.parent = root
        elif node.data > root.data:
            root.right_child = self.insert(root.right_child, value)
            root.right_child.parent = root
        return root

    def __print_tree(self, node, prefix=None):
        if prefix is None:
            prefix, prefix_left_child = '', ''
        else:
            prefix = prefix.replace(self.prefix_branch, self.prefix_trunk)
            prefix = prefix.replace(self.prefix_leaf, self.prefix_empty)
            prefix_left_child = prefix.replace(self.prefix_leaf, self.prefix_empty)
        if self.has_child(node):
            if node.right_child is not None:
                print(prefix + self.prefix_branch + self.prefix_right + str(node.right_child.data))
                if self.has_child(node.right_child):
                    self.__print_tree(node.right_child, prefix + self.prefix_branch + ' ')
            else:
                print(prefix + self.prefix_branch + self.prefix_right)
            if node.left_child is not None:
                print(prefix + self.prefix_leaf + self.prefix_left + str(node.left_child.data))
                if self.has_child(node.left_child):
                    prefix_left_child += '  '
                    self.__print_tree(node.left_child, self.prefix_leaf + prefix_left_child)
            else:
                print(prefix + self.prefix_leaf + self.prefix_left)

    def has_child(self, node):
        return node.left_child is not None or node.right_child is not None

    def __str__(self):
        return str(self.__class__)

The above code implements a node class Node and a binary search tree class SearchBinaryTree. In SearchBinaryTree, the is_empty() method for judging whether the binary search tree is empty, a pair of root() methods for instance objects to call, the show_tree() method for printing the binary search tree in a tree structure, and adding data to it are implemented in SearchBinaryTree The insert(root, value) method in the binary search tree. 

Now first add some nodes to the tree for later deletion methods.

if __name__ == '__main__':
    tree = SearchBinaryTree()
    data = [50, 77, 55, 29, 10, 30, 66, 80, 51, 18, 90, 78, 79]
    for i in data:
        tree.insert(tree.root, i)
    tree.show_tree()

operation result:

--------------------
50
├─R─77
| ├─R─80
| | ├─R─90
| | └─L─78
| |   ├─R─79
| |   └─L─
| └─L─55
|   ├─R─66
|   └─L─51
└─L─29
  ├─R─30
  └─L─10
    ├─R─18
    └─L─
--------------------

The binary search tree after adding data is as follows:

After adding the node, continue to prepare the method to be used when deleting the node. The method height(root) that returns the height of the binary search tree is used to determine which level of the tree is the deleted node. The method search (root, data) to find a node in a binary search tree, to delete a node from the binary search tree, first ensure that the node belongs to the binary search tree, so first search for whether the node is in the binary search tree . The method get_max(root) to return the node with the largest value in the binary search tree and the method get_min(root) to return the node with the smallest value are used to find the predecessor or successor of the deleted node.

    def height(self, root):
        """二叉树的深度"""
        if root.data is None:
            return 0
        if root.left_child is None and root.right_child is None:
            return 1
        if root.left_child is not None and root.right_child is None:
            return 1 + self.height(root.left_child)
        if root.left_child is None and root.right_child is not None:
            return 1 + self.height(root.right_child)
        if root.left_child is not None and root.right_child is not None:
            return 1 + max(self.height(root.left_child), self.height(root.right_child))

    def search(self, root, data):
        """二叉搜索树的查询操作"""
        if root is None:
            return
        if root.data == data:
            return root
        elif data < root.data:
            return self.search(root.left_child, data)
        elif data > root.data:
            return self.search(root.right_child, data)

    def get_max(self, root):
        """查找二叉搜索树值最大的节点"""
        if self.is_empty():
            return
        return self.get_max(root.right_child) if root.right_child else root

    def get_min(self, root):
        """查找二叉搜索树值最小的节点"""
        if self.is_empty():
            return
        return self.get_min(root.left_child) if root.left_child else root

Two, the binary search tree delete node

If the node to be deleted is a leaf node, it is easier to handle, find this node, and then delete it. If the deleted node is not a leaf node, the node has a left subtree or a right subtree, or both, after deleting the node, the left subtree and the right subtree will be "broken" from the tree along with the node, then Not only delete the node, but delete all the subtrees. Therefore, when deleting a non-leaf node, you must select a node from the subtree to fill the position of the deleted node to avoid tree breaks, and avoid "implication" to other nodes, and ensure that the binary tree after the node is deleted is still one. A binary search tree satisfies the characteristics of a binary search tree.

From simple to complex, according to whether the deleted node has left and right subtrees, the deletion of the binary search tree can be divided into three situations. The deleted node is a leaf node, the deleted node has only one subtree, and the deleted node has two subtrees. .

1. The deleted node is a leaf node, that is, the deleted node does not have a left subtree and a right subtree. After the leaf node is deleted, the structure of the entire tree will not be destroyed, as long as the node is found, and the node is "disconnected" from the binary tree (the pointer of the parent node points to empty). In the binary search tree after adding data above, randomly select a leaf node, such as 66.

    def _delete_no_child(self, node):
        """删除叶节点"""
        if node == self.root:
            self.root = None
            return
        if node == node.parent.left_child:
            node.parent.left_child = None
        else:
            node.parent.right_child = None
        node.parent = None

_delete_no_child(node): Method to delete leaf nodes. This method is only part of the delete function, so add an underscore in front to indicate that it will not be used directly after the delete function is fully implemented.

    node = tree.search(tree.root, 66)
    tree._delete_no_child(node)
    tree.show_tree()

operation result:

--------------------
50
├─R─77
| ├─R─80
| | ├─R─90
| | └─L─78
| |   ├─R─79
| |   └─L─
| └─L─55
|   ├─R─
|   └─L─51
└─L─29
  ├─R─30
  └─L─10
    ├─R─18
    └─L─
--------------------

The binary search tree structure after deleting node 66 is as shown in the figure below.

2. The deleted node has only one subtree, that is, the deleted node has only the left subtree or only the right subtree. After deleting the node, in order to ensure that the subtree of the node is not "implicated", the subtree of the node should be "filled". If you delete node 10 in the figure below, the deleted node is the left child node of its parent node, and the deleted node has a right child tree. After deleting the node, the right child tree of the deleted node will be directly filled in to become the parent node's Left subtree.

    def _delete_one_child(self, node):
        """删除有一个子节点的节点"""
        if node.left_child:
            if node.parent and node.parent.left_child == node:
                node.left_child.parent = node.parent
                node.parent.left_child = node.left_child
            elif node.parent and node.parent.right_child == node:
                node.left_child.parent = node.parent
                node.parent.right_child = node.left_child
            else:
                self.root = node.left_child
                node.left_child.parent = None
                node.left_child = None
        else:
            if node.parent and node.parent.left_child == node:
                node.right_child.parent = node.parent
                node.parent.left_child = node.right_child
            elif node.parent and node.parent.right_child == node:
                node.right_child.parent = node.parent
                node.parent.right_child = node.right_child
            else:
                self.root = node.right_child
                node.right_child.parent = None
                node.right_child = None
        node.left_child = None
        node.parent = None

_delete_one_child(node): Delete a node with only one child tree. In the same way, this method is only part of the delete function, so add an underscore in front to indicate that the delete function will not be used directly after the delete function is fully realized.

    node = tree.search(tree.root, 10)
    tree._delete_one_child(node)
    tree.show_tree()

operation result:

--------------------
50
├─R─77
| ├─R─80
| | ├─R─90
| | └─L─78
| |   ├─R─79
| |   └─L─
| └─L─55
|   ├─R─66
|   └─L─51
└─L─29
  ├─R─30
  └─L─18
--------------------

The binary search tree structure after deleting node 10 is as shown in the figure below.

3. The deleted node has two subtrees, that is, the deleted node has both a left subtree and a right subtree. After deleting the node, in order to prevent the two subtrees from "breaking" from the tree, a node must be found to fill the position of the deleted node. If you delete node 77 in the figure below, you must find a node to fill in, otherwise the two subtrees will also be deleted.

In order to ensure that the binary tree is still a binary search tree after the node is deleted, there are two ways to choose the complement node. Choose the successor or successor of the deleted node. The successor node must exist in the left subtree, and the successor node must exist. In the right subtree, you can choose either way. This article selects the successor node.

So what are successor nodes and predecessor nodes? If the nodes in the binary search tree are arranged in the ascending order of the stored value, the node after the current node is the successor node, and the node before the current node is the predecessor node. The successor node of node 77 is 78.

After deleting the node, find its successor node, and fill the successor node to the position of the deleted node, thus ensuring that the two subtrees of the deleted node are not broken. But it is not over yet. After the successor node is filled, it is equivalent to deleting the successor node from its original position. After the node 78 is added to the position of 77, it is equivalent to deleting the node 78 from its original position.

Therefore, the successor node must be processed. The successor node may be a leaf node. Case 1 may be used. The successor node may have a right subtree. Case 2 may be used, such as node 78. The successor node cannot have a left subtree, so you don't need to consider it.

Why must the successor node have no left subtree? Assuming that the successor node has a left subtree, according to the characteristics of the binary search tree, the value of the successor node is greater than the current node, the successor node must be in the right subtree of the current node, and the value of the node in the left subtree of the successor node is less than The value of the successor node, in this way, there are nodes larger than the current node and smaller than the successor node, which contradicts the definition of the successor node, so the hypothesis does not hold.

Of course, this is not to say that the successor nodes of all nodes in the binary search tree do not have a left subtree, but when a node has two subtrees, its successor node cannot have a left subtree.

Up to this point, deleting node 77 is divided into two steps. The first step is to find the successor node 78 and fill the position of node 78 to the position of 77, and the second step is to delete node 78.

    def delete(self, value):
        """二叉搜索树删除节点"""
        if self.height(self.root) >= 1:
            node_delete = self.search(self.root, value)
            if node_delete:
                self._delete(node_delete)
            else:
                raise KeyError("Error, value not in this tree")
        else:
            raise KeyError("Error, value not in this tree")

    def _delete(self, node):
        """删除节点"""
        if not node.left_child and not node.right_child:
            self._delete_no_child(node)
        elif node.left_child and not node.right_child or not node.left_child and node.right_child:
            self._delete_one_child(node)
        else:
            rear_node = self.get_min(node.right_child)
            node.data = rear_node.data
            self._delete(rear_node)

_delete(node): The method of deleting a node. In this method, the successor node rear_node of the current node is found through the get_min() method, and then the bit-filling operation is performed. The operation of deleting a successor node only needs to be recursive once, because the method of deleting a successor node must belong to one of the previous two cases.

delete(node): The general method of deleting a node. As mentioned earlier, to delete a node, first the node must belong to the current binary search tree. Therefore, you cannot specify a node to delete. You can only search the tree based on the value stored in the node, and then perform the delete operation when you find it.

    tree.delete(77)
    tree.show_tree()

operation result:

--------------------
50
├─R─78
| ├─R─80
| | ├─R─90
| | └─L─79
| └─L─55
|   ├─R─66
|   └─L─51
└─L─29
  ├─R─30
  └─L─10
    ├─R─18
    └─L─
--------------------

The binary search tree structure after deleting node 77 is shown in the figure below.

Three, the complete code

# coding=utf-8
class Node(object):
    """节点类"""
    def __init__(self, data, left_child=None, right_child=None):
        self.data = data
        self.parent = None
        self.left_child = left_child
        self.right_child = right_child


class SearchBinaryTree(object):
    """二叉树类"""
    def __init__(self):
        self.__root = None
        self.prefix_branch = '├'
        self.prefix_trunk = '|'
        self.prefix_leaf = '└'
        self.prefix_empty = ''
        self.prefix_left = '─L─'
        self.prefix_right = '─R─'

    def is_empty(self):
        return not self.__root

    @property
    def root(self):
        return self.__root

    @root.setter
    def root(self, value):
        self.__root = value if isinstance(value, Node) else Node(value)

    def show_tree(self):
        if self.is_empty():
            print('空二叉树')
            return
        print('-' * 20)
        print(self.__root.data)
        self.__print_tree(self.__root)
        print('-' * 20)

    def insert(self, root, value):
        """二叉搜索树插入节点-递归"""
        node = value if isinstance(value, Node) else Node(value)
        if self.is_empty():
            self.root = node
            return
        if root is None:
            root = node
        elif node.data < root.data:
            root.left_child = self.insert(root.left_child, value)
            root.left_child.parent = root
        elif node.data > root.data:
            root.right_child = self.insert(root.right_child, value)
            root.right_child.parent = root
        return root

    def delete(self, value):
        """二叉搜索树删除节点"""
        if self.height(self.root) >= 1:
            node_delete = self.search(self.root, value)
            if node_delete:
                self._delete(node_delete)
            else:
                raise KeyError("Error, value not in this tree")
        else:
            raise KeyError("Error, value not in this tree")

    def _delete(self, node):
        """删除节点"""
        if not node.left_child and not node.right_child:
            self._delete_no_child(node)
        elif node.left_child and not node.right_child or not node.left_child and node.right_child:
            self._delete_one_child(node)
        else:
            rear_node = self.get_min(node.right_child)
            node.data = rear_node.data
            self._delete(rear_node)

    def _delete_no_child(self, node):
        """删除叶节点"""
        if node == self.root:
            self.root = None
            return
        if node == node.parent.left_child:
            node.parent.left_child = None
        else:
            node.parent.right_child = None
        node.parent = None

    def _delete_one_child(self, node):
        """删除有一个子节点的节点"""
        if node.left_child:
            if node.parent and node.parent.left_child == node:
                node.left_child.parent = node.parent
                node.parent.left_child = node.left_child
            elif node.parent and node.parent.right_child == node:
                node.left_child.parent = node.parent
                node.parent.right_child = node.left_child
            else:
                self.root = node.left_child
                node.left_child.parent = None
                node.left_child = None
        else:
            if node.parent and node.parent.left_child == node:
                node.right_child.parent = node.parent
                node.parent.left_child = node.right_child
            elif node.parent and node.parent.right_child == node:
                node.right_child.parent = node.parent
                node.parent.right_child = node.right_child
            else:
                self.root = node.right_child
                node.right_child.parent = None
                node.right_child = None
        node.left_child = None
        node.parent = None

    def height(self, root):
        """二叉树的深度"""
        if root.data is None:
            return 0
        if root.left_child is None and root.right_child is None:
            return 1
        if root.left_child is not None and root.right_child is None:
            return 1 + self.height(root.left_child)
        if root.left_child is None and root.right_child is not None:
            return 1 + self.height(root.right_child)
        if root.left_child is not None and root.right_child is not None:
            return 1 + max(self.height(root.left_child), self.height(root.right_child))

    def search(self, root, data):
        """二叉搜索树的查询操作"""
        if root is None:
            return
        if root.data == data:
            return root
        elif data < root.data:
            return self.search(root.left_child, data)
        elif data > root.data:
            return self.search(root.right_child, data)

    def get_max(self, root):
        """查找二叉搜索树值最大的节点"""
        if self.is_empty():
            return
        return self.get_max(root.right_child) if root.right_child else root

    def get_min(self, root):
        """查找二叉搜索树值最小的节点"""
        if self.is_empty():
            return
        return self.get_min(root.left_child) if root.left_child else root

    def __print_tree(self, node, prefix=None):
        if prefix is None:
            prefix, prefix_left_child = '', ''
        else:
            prefix = prefix.replace(self.prefix_branch, self.prefix_trunk)
            prefix = prefix.replace(self.prefix_leaf, self.prefix_empty)
            prefix_left_child = prefix.replace(self.prefix_leaf, self.prefix_empty)
        if self.has_child(node):
            if node.right_child is not None:
                print(prefix + self.prefix_branch + self.prefix_right + str(node.right_child.data))
                if self.has_child(node.right_child):
                    self.__print_tree(node.right_child, prefix + self.prefix_branch + ' ')
            else:
                print(prefix + self.prefix_branch + self.prefix_right)
            if node.left_child is not None:
                print(prefix + self.prefix_leaf + self.prefix_left + str(node.left_child.data))
                if self.has_child(node.left_child):
                    prefix_left_child += '  '
                    self.__print_tree(node.left_child, self.prefix_leaf + prefix_left_child)
            else:
                print(prefix + self.prefix_leaf + self.prefix_left)

    def has_child(self, node):
        return node.left_child is not None or node.right_child is not None

    def __str__(self):
        return str(self.__class__)


if __name__ == '__main__':
    tree = SearchBinaryTree()
    data = [50, 77, 55, 29, 10, 30, 66, 80, 51, 18, 90, 78, 79]
    for i in data:
        tree.insert(tree.root, i)
    # tree.show_tree()

    # node = tree.search(tree.root, 66)
    # tree._delete_no_child(node)
    # tree.show_tree()

    # node = tree.search(tree.root, 10)
    # tree._delete_one_child(node)
    # tree.show_tree()

    tree.delete(80)
    tree.show_tree()

 

 

Guess you like

Origin blog.csdn.net/weixin_43790276/article/details/105777348