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
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:
- Enter the node x to be deleted and the binary search tree T.
- Start the search in the right subtree of node x: find the minimum node H by going right and left;
- 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:
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
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
- <<Introduction to Algorithms Third Edition>>
- http://algs4.cs.princeton.edu/32bst/