Python data structure binary search tree

Definition of binary search tree

A binary search tree is organized as a binary tree. In addition to the key , each node also includes information such as left child , right child , parent node , etc. BST satisfies the restriction: for any node X, its left subtree contains keywords The maximum value <= X.key , the minimum value of the right subtree keyword >= X.key This relationship is expressed as follows
Binary
According to the definition in the above figure, an example of a binary search tree is

Binary tree operations

  • Inquire
  • insert
  • delete

query (search)

Binary tree search uses a recursive way to query, according to the definition of binary search tree: the left subtree stores small values, and the right subtree stores large values. A complete binary search diagram is as follows,

which can be written as pseudo code

TREE-SEARCH(x, k)
  if x == NULL  or k == x.key
    return x  
  if k < x.key
    return TREE-SEARCH(x.left)
  if k > x.key
    return TREE-SEARCH(x.right)

Convert to python code

def _get(self, key, node):
        if node is None:
            return None

        if key < node.key:
            return self._get(key, node.left)
        elif key > node.key:
            return self._get(key, node.right)
        else:
            return node.val
def get(self, key):
        """
        Return the value paired with 'key'

        Worst Case Complexity: O(N)

        Balanced Tree Complexity: O(lg N)
        """
        return self._get(key, self.root)

insert

Insertion and deletion are a little more complicated than query, because the operation will cause the size of the binary search tree to change, which will change the structure of the dynamic collection. Insertion is slightly easier to implement than deletion. Insertion is divided into two parts

  • query insert node
  • Change the data structure near the target node

The schematic diagram of the insertion process is as follows

The corresponding pseudocode is as follows, input node z , z.key = v , z.left = NULL , z.right = NULL .

TREE-INSERT(T, x)
  y = NULL
  x = T.root   # 从根节点开始
  while x != NULL
    y = x      # 保存上一节点
    if z.key < x.key # 往左
      x = x.left
    else             # 往右
      x = x.right

  z.p = y        # 父节点
  if y == NULL   # tree T 为空
    T.root = z
  else if z.key < y.key
    y.left = z
  else y.right = z

The running complexity of the program depends on the shape of the binary tree. The running time of insertion depends on the height h

of the binary search tree , and the running time of the program is O(h) , so the shape of the binary tree directly affects the running time of the algorithm.

The python code is implemented as

def _put(self, key, val, node):

        # If we hit the end of a branch, create a new node
        if node is None:
            return Node(key, val)

        # Follow left branch
        if key < node.key:
            node.left = self._put(key, val, node.left)
        # Follow right branch
        elif key > node.key:
            node.right = self._put(key, val, node.right)
        # Overwrite value
        else:
            node.val = val

        node.size_of_subtree = self._size(node.left) + self._size(node.right)+1
        return node
def put(self, key, val):
          """
        Add a new key-value pair.

        Worst Case Complexity: O(N)

        Balanced Tree Complexity: O(lg N)
        """
        self.root = self._put(key, val, self.root)

delete

There are three cases of deletion:

  • If the deleted node x has no children, just delete it directly;
  • If delete node x has 1 child, replace that node position with child;
  • If deleting node x has 2 children, the situation is a bit more complicated. The key is to find the successor of node x . The successor of node z has the smallest key value in the right subtree of node z. The operation in this case divides for the following steps:

    1. Enter the node x to be deleted and the binary search tree T.
    2. Start the search in the right subtree of node x: find the minimum node H by going right and left;
    3. The right child of H is the parent node of H, and the left child of H is the left child of X;

The schematic diagram is as follows, it should be clear at a glance:

According to the description above, the deleted pseudocode can be divided into two parts:

  1. To move a subtree, replace a subtree with a subtree that becomes the parent's child node.

    TRANSPLANT(T, u, v)
    if u.p == NULL
    T.root = v
    else if u = u.p.left
    u.p.left = v
    else u.p.right = v
    
    if v!= NULL
    v.p = u.p
  2. Complete the deletion process of the binary search tree according to the first step:

    TREE-DELETE(T, z)
    if z.left = NULL
    TRANSPLANT(T, z, z.right)
    else if (z.right == NULL)
    TRANSPLANT(T, z, z.left)
    else
    y = TREE-MINIMUM(z.right)
    if y.p != z
    TRANSPLANT(T, y, y.right)
    y.right = z.right
    y.right.p = y
    TRANSPLANT(T, z, y)
    y.left = z.left
    y.left.p = y

    Implemented in python as follows:

    def _delete(self, key, node):
    if node is None:
       return None
    if key < node.key:
       node.left = self._delete(key, node.left)
    elif key > node.key:
       node.right = self._delete(key, node.right)
    
    else:
       if node.right is None:
           return node.left
       elif node.left is None:
           return node.right
       else:
           old_node = node
           node = self._ceiling_node(key, node.right)
           node.right = self._delete_min(old_node.right)
           node.left = old_node.left
    node.size_of_subtree = self._size(node.left) + self._size(node.right)+1
    return node
    def _delete_min(self, node):
    if node.left is None:
       return node.right
    
    node.left = self._delete_min(node.left)
    node.size_of_subtree = self._size(node.left) + self._size(node.right)+1
    return node
    def _ceiling_node(self, key, node):
    """
    Returns the node with the smallest key that is greater than or equal to
    the given value 'key'
    """
    if node is None:
       return None
    
    if key < node.key:
       # Ceiling is either in left subtree or is this node
       attempt_in_left = self._ceiling_node(key, node.left)
       if attempt_in_left is None:
           return node
       else:
           return attempt_in_left
    elif key > node.key:
       # Ceiling must be in right subtree
       return self._ceiling_node(key, node.right)
    else:
       # Keys are equal so ceiling is node with this key
       return node

references

  1. <<Introduction to Algorithms Third Edition>>
  2. http://algs4.cs.princeton.edu/32bst/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325528897&siteId=291194637