Python implements Binary Search Tree and python Binary Search Tree
Binary Search Tree
We already know two different methods for getting key-value pairs in a set. Recall how these sets implement ADT (abstract data type) MAP. We will discuss two adt map implementation methods: list-based Binary lookup and hash tables. In this section, we will learn the binary search tree, which is another Map set of keys pointing to values. In this case, we do not need to consider the actual position of elements in the tree, however, it is more efficient to search using binary trees.
Search Tree operations
Before studying this implementation method, let's review the interfaces provided by adt map. We will notice that this interface is very similar to the Python dictionary.
- Map () creates a new empty Map set.
- Put (key, val) adds a new key-value pair to Map. If the key is already in the Map, a new value is used to replace the old value.
- Get (key) provides a key to return the data stored in Map or None.
- Del uses the del map [key] statement to delete key-value pairs from Map.
- Len () returns the number of key-value pairs saved in Map.
- If the given key is in Map, use the key in map statement to return True.
Search tree implementation
A binary search tree. If the key values in the left subtree are smaller than the parent node, and the key values in the right subtree are greater than the attributes of the parent node, we call this tree a BST search tree. As described earlier, when we implement Map, the BST method will guide us to achieve this. Figure 1 shows the feature of the binary search tree. The displayed key is not associated with any value. Note that this attribute applies to each parent node and child node. All keys in the left subtree are smaller than those in the root node. All keys in the right subtree are greater than those in the root node.
Figure 1: A simple binary search tree
Now you know what a binary search tree is. Let's look at how to construct a binary search tree. In the search tree, we insert these key values in order of the nodes shown in Figure 1. in figure 1, the nodes in the search tree exist:, 31, 73. Because 70 is the first value inserted into the tree, it is the root node. Next, 31 is less than 70, so it is the left subtree of 70. Next, 93 is greater than 70, so it is the right subtree of 70. We now fill two layers of the tree, so the next key value will be the left or right subtree of 31 or 93. Because 94 is greater than 70 and 93, it becomes the right subtree of 93. Similarly, 14 is smaller than 70 and 31, so it becomes the left subtree of 31. 23 is smaller than 31, so it must be the left subtree of 31. However, it is greater than 14, so it is the right subtree of 14.
To implement the binary search tree, we will use the node and reference method, which is similar to the process of implementing the linked list and Expression Tree. Because we must be able to create and use an empty binary search tree, we will use two classes. The first class is called BinarySearchTree, and the second class is called TreeNode. The BinarySearchTree class has a reference to the TreeNode class as the root of the binary search tree. In most cases, the external method defined by the external class only needs to check whether the tree is empty. If there are nodes on the tree, the BinarySearchTree class must contain private methods to define the root as a parameter. In this case, if the fruit tree is empty or we want to delete the root of the tree, we must adopt special operations. The code for the constructor of the BinarySearchTree class and some other functions is shown in Listing 1.
Listing 1
class BinarySearchTree: def __init__(self): self.root = None self.size = 0 def length(self): return self.size def __len__(self): return self.size def __iter__(self): return self.root.__iter__()
The TreeNode class provides many auxiliary functions, making the BinarySearchTree class method easier to implement. As shown in Listing 2, the structure of a tree node is implemented by these auxiliary functions. As you can see, these helper functions can divide a node as the Left or Right child and the child node type based on their locations. The TreeNode class clearly tracks the attributes of each parent node. When we discuss the implementation of the delete operation, you will understand why this is important.
Another interesting thing about the TreeNode implementation in Listing 2 is that we use optional Python parameters. The optional parameters allow us to create a tree node in several different situations. Sometimes we want to create a new tree node, even if we already have a parent node and a child node. Like existing parent and child nodes, we can use the parent node and child node as parameters. Sometimes we also create a tree containing key-value pairs. We will not pass any parameters of the parent node or child node. In this case, the default value of the optional parameter is used.
Listing 2
class TreeNode: def __init__(self,key,val,left=None,right=None, parent=None): self.key = key self.payload = val self.leftChild = left self.rightChild = right self.parent = parent def hasLeftChild(self): return self.leftChild def hasRightChild(self): return self.rightChild def isLeftChild(self): return self.parent and self.parent.leftChild == self def isRightChild(self): return self.parent and self.parent.rightChild == self def isRoot(self): return not self.parent def isLeaf(self): return not (self.rightChild or self.leftChild) def hasAnyChildren(self): return self.rightChild or self.leftChild def hasBothChildren(self): return self.rightChild and self.leftChild def replaceNodeData(self,key,value,lc,rc): self.key = key self.payload = value self.leftChild = lc self.rightChild = rc if self.hasLeftChild(): self.leftChild.parent = self if self.hasRightChild(): self.rightChild.parent = self
Now we have the BinarySearchTree and TreeNode classes. It is time to write a put method so that we can build a binary search tree. The put method is a method of the BinarySearchTree class. This method checks whether the tree has roots. If not, we will create a new tree node and set it as the root of the tree. If there is already a root node, we will call it ourselves for recursion and use the auxiliary function _ put to search the tree according to the following algorithms:
Start from the root node of the tree and search for a binary tree to compare the new key value and the key value of the current node. If the new key value is smaller than the current node, search for the left subtree. If the new key is greater than the current node, search for the right subtree.
When the left (or right) subtree is not found, the position in the tree is to set the position of the new node.
Add a node to the tree, create a new TreeNode object, and insert this object into the previous node of This vertex.
Listing 3 shows the Python code for inserting a new node into the tree. Follow these steps to compile a recursive algorithm for the _ put function. Note: When a new subtree is inserted, the current node (CurrentNode) is passed as the parent node to the new subtree.
An important issue for executing inserts is that duplicate key values cannot be correctly processed, and our tree implements key value replication, it creates a new node with the same key value as the original node in the right subtree. The consequence is that new nodes will not be found during the search process. We use a better way to insert duplicate key values. The old values are replaced by the values associated with the new key. We will leave you with the repair of this error as an exercise.
Listing 3
def put(self,key,val): if self.root: self._put(key,val,self.root) else: self.root = TreeNode(key,val) self.size = self.size + 1def _put(self,key,val,currentNode): if key < currentNode.key: if currentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild = TreeNode(key,val,parent=currentNode) else: if currentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild = TreeNode(key,val,parent=currentNode)
With the implementation of the put method, we can easily use the _ setitem _ method to overload [] as an operator to call the put method (see Listing 4 ). This allows us to write python statements like myZipTree ['pluth U'] = 55446, which looks like a Python dictionary.
Listing 4
def __setitem__(self,k,v): self.put(k,v)
Figure 2 illustrates the process of inserting a new node into a binary search tree. The gray node shows the order of traversing Tree nodes during the insertion process.
Figure 2: Insert a node with a key value of 19
Once the tree is constructed, the next task is to retrieve a given key value. The get method is easier than the put method because it only needs to Recursively search the tree until it finds unmatched leaf nodes or finds a matched key value. After a matched key value is found, the value in the node is returned.
Listing 5 displays the get, _ get, and _ getitem _ codes. The Code searched using the _ get method has the same logic as the put Method for Selecting the left or right subtree. Note that the _ get method returns the get value in TreeNode. The _ get method can be used as a flexible and effective method to provide parameters for other methods of BinarySearchTree that may need to use data in TreeNode.
By implementing the _ getitem _ method, we can write a Python statement that looks like we access the dictionary. In fact, we only operate on the binary search tree, for example, Z = myziptree ['fargo']. As you can see, the __getitem _ method is calling get.
Listing 5
def get(self,key): if self.root: res = self._get(key,self.root) if res: return res.payload else: return None else: return Nonedef _get(self,key,currentNode): if not currentNode: return None elif currentNode.key == key: return currentNode elif key < currentNode.key: return self._get(key,currentNode.leftChild) else: return self._get(key,currentNode.rightChild)def __getitem__(self,key): return self.get(key)
With get, we can write a _ contains _ method of BinarySearchTree to implement the operation. The __contains _ method simply calls the get method. If it has a return value, True is returned, if it is None, False is returned. See Listing 6.
Listing 6
def __contains__(self,key): if self._get(key,self.root): return True else: return False
Review the _ contains _ overload operator, which allows us to write such statements:
if 'Northfield' in myZipTree: print("oom ya ya")
Articles you may be interested in:
- Python binary tree implementation example
- Implementation of python binary tree traversal
- Introduction to python Data Structure Tree and binary tree
- Example of building a binary tree in python Data Structure
- Python Data Structure-binary tree traversal instance
- Implement binary heap in Python