Binary Find Tree Properties
Before starting this article, let's first review how linked lists and array elements are found. Chain lists and arrays must find elements in turn by traversing back and forth from the top node. This is very inefficient to find. If you use a binary search tree, it will greatly improve your search efficiency. Next, let's look at the rules for creating a binary search tree.
Properties and Rules of Binary Find Trees
First let's look at what a binary lookup tree is: it is either an empty tree or a binary tree with the following properties: if its left subtree is not empty, the values of all nodes in the left subtree are less than its root node; if its right subtree is not empty, the values of all nodes in the right subtree are greater than its root node;Its left and right subtrees are also binary sort trees. The following image is a binary search tree:
Since the value of the left subtree of a node is smaller than that of the node, the value of the right subtree is larger than that of the node. So when we look for a value that is larger than the current node, we go to the right subtree of the node and continue to look for it, which greatly improves the efficiency of the search. From the next exciting moment on, let's write code to achieve this numberData structure.
Creation of a Binary Find Tree
Node Class of Binary Find Tree
From the observation of the graph, we find that a binary tree is actually composed of one node and its relationship. According to the object-oriented idea, we design a node class to describe the node.
Node class API design:
Class name | Node<Key,Value> |
---|---|
Construction method | Node (Key, Value value, Node left, Node right): Create a Node object |
Member variables | 1.public Node left: Record left child node 2.public Node right: Record right child node 3.Public Key:Storage Key 4.public Value value: Stored value |
code implementation
private class Node<Key,Value>{ //Storage key public Key key; //Stored Value private Value value; //Record left child node public Node left; //Record Right Child Node public Node right; public Node(Key key, Value value, Node left, Node right) { this.key = key; this.value = value; this.left = left; this.right = right; } }
Binary Lookup Tree API Design
Class name | BinaryTree,Value value> |
---|---|
Construction method | BinaryTree(): Create a BinaryTree object |
Member variables | 1.private Node root: Record root node 2.private int N: Number of elements in the record tree |
Member Method | 1.Public void put (Key, Value value): Insert a key-value pair into the tree 2.private Node put (Node x, Key, Value val): Add a key-value pair to the specified tree X and return the added new tree 3.public Value get(Key key): Find the corresponding value from the tree based on the key 4.private Value get(Node x, Key key): Find the value of the key from the specified tree x 5.public void delete(Key key): Delete the corresponding key-value pair in the tree according to the key 6.private Node delete(Node x, Key key): Deletes the key-value pair whose key is key on the specified tree X and returns the deleted new tree 7.public int size(): Gets the number of elements in the tree |
Binary Find Tree Implementation
The insertion method put implements the idea:
- If there is no node in the current tree, use the new node directly as the root node
- If the current tree is not empty, start at the root node:
- If the key of the new node is less than the key of the current node, the left child of the current node is continued to be found.
- If the key of the new node is greater than the key of the current node, the right child of the current node is continued to be found.
- If the key of the new node equals the key of the current node, such a node already exists in the tree, and the value of the node can be replaced.
The process is as follows:
- Store only the first element: 8-Liu 8
- Store the second element: 7-Tanaka
- Store the third element: 9-Zheng Jiu
-
Store the fourth element: 3-King Five
-
Store the fifth element: 9-like a flower (directly replacing Zheng Jiu)
Query method get implementation idea:
Start at the root node:
- If the key to be queried is less than the key of the current node, continue to find the left sub-node of the current node.
- If the key to be queried is larger than the key of the current node, continue to find the right sub-node of the current node.
- If the key to be queried equals the key of the current node, the value of the current node is returned in the tree.
Delete method delete implementation idea:
- Find the deleted node;
- Find the smallest node minNode in the right subtree of the deleted node
- Delete the smallest node in the right subtree
- Let the left subtree of the deleted node be called the left subtree of the minimum node minNode, and the right subtree of the deleted node be called the right subtree of the minimum node minNode
- Let the parent of the deleted node point to the smallest node, minNode
process
- Delete element 10
-
Find the smallest node in the right subtree of the deleted element
-
The parent of the deleted node points to the smallest node in the right subtree of the deleted node
Code:
//Binary Tree Code public class BinaryTree<Key extends Comparable<Key>, Value> { //Record Root Node private Node root; //Number of elements in the record tree private int N; //Gets the number of elements in the tree public int size() { return N; } //Add the element key-value to the tree public void put(Key key, Value value) { root = put(root, key, value); } //Add key-value to the specified tree x and return the new tree after adding the element private Node put(Node x, Key key, Value value) { if (x == null) { //Number + 1 N++; return new Node(key, value, null, null); } int cmp = key.compareTo(x.key); if (cmp > 0) { //The key of the new node is larger than the key of the current node, continue to find the right child of the current node x.right = put(x.right, key, value); } else if (cmp < 0) { //The key of the new node is less than the key of the current node. Continue to find the left child of the current node x.left = put(x.left, key, value); } else { //The key of the new node is equal to the key of the current node, replacing the value of the current node x.value = value; } return x; } //The value corresponding to the specified key in the query tree public Value get(Key key) { return get(root, key); } //Find the value of the key from the specified tree x public Value get(Node x, Key key) { if (x == null) { return null; } int cmp = key.compareTo(x.key); if (cmp > 0) { //If the key to be queried is larger than the key of the current node, continue to find the right sub-node of the current node. return get(x.right, key); } else if (cmp < 0) { //If the key to be queried is less than the key of the current node, continue to find the left sub-node of the current node. return get(x.left, key); } else { //If the key to be queried equals the key of the current node, the value of the current node is returned in the tree. return x.value; } } //Delete the value corresponding to the key in the tree public void delete(Key key) { root = delete(root, key); } //Deletes the value corresponding to the key in the specified tree x and returns the deleted new tree public Node delete(Node x, Key key) { if (x == null) { return null; } int cmp = key.compareTo(x.key); if (cmp > 0) { //The key of the new node is larger than the key of the current node, continue to find the right child of the current node x.right = delete(x.right, key); } else if (cmp < 0) { //The key of the new node is less than the key of the current node. Continue to find the left child of the current node x.left = delete(x.left, key); } else { //The key of the new node is equal to the key of the current node, and the current x is the node to be deleted //1. If the right subtree of the current node does not exist, return directly to the left subtree of the current node if (x.right == null) { return x.left; } //2. If the left subtree of the current node does not exist, return directly to the right subtree of the current node if (x.left == null) { return x.right; } //3. Both left and right subtrees of the current node exist //3.1 Find the smallest node in the right subtree Node minNode = x.right; while (minNode.left != null) { minNode = minNode.left; } //3.2 Delete the smallest node in the right subtree Node n = x.right; while (n.left != null) { if (n.left.left == null) { n.left = null; } else { n = n.left; } } //3.3 Let the left subtree of the deleted node be called the left subtree of the minimum node minNode, and the right subtree of the deleted node be called the right subtree of the minimum node minNode. minNode.left = x.left; minNode.right = x.right; //3.4 Let the parent of the deleted node point to the smallest node, minNode x = minNode; //Number-1 N--; } return x; } private class Node { //Storage key public Key key; //Stored Value private Value value; //Record left child node public Node left; //Record Right Child Node public Node right; public Node(Key key, Value value, Node left, Node right) { this.key = key; this.value = value; this.left = left; this.right = right; } } } //Test Code public class Test { public static void main(String[] args) throws Exception { BinaryTree<Integer, String> bt = new BinaryTree<>(); bt.put(4, "Erhar"); bt.put(1, "Zhang San"); bt.put(3, "Li Si"); bt.put(5, "King Five"); System.out.println(bt.size()); bt.put(1,"Third"); System.out.println(bt.get(1)); System.out.println(bt.size()); bt.delete(1); System.out.println(bt.size()); } }
Binary Find Tree Other Easy Ways
Find the smallest key in a binary tree
In some cases, we need to find the minimum value of the key that stores all the elements in the tree. For example, our tree stores the rank and name data of the students. Then we need to find out what the lowest rank is. Here we design two ways to do this:
public Key min() | Find the smallest key in the tree |
---|---|
private Node min(Node x) | Find the node where the smallest key is located in the specified tree x |
//Find the smallest key in the whole tree public Key min(){ return min(root).key; } //Find the node where the smallest key in the specified tree x is located private Node min(Node x){ if (x.left!=null){ return min(x.left); }else{ return x; } }
Find the largest key in a binary tree
In some cases, we need to find out the maximum value of the key that stores all the elements in the tree, for example, if our tree stores the student's grades and the student's name, then we need to find out what the highest score is? Here we also design two ways to do this:
public Key max() | Find the largest key in the tree |
---|---|
public Node max(Node x) | Find the node where the largest key is located in the specified tree x |
//Find the largest key in the whole tree public Key max(){ return max(root).key; } //Find the node with the largest key in the specified tree x public Node max(Node x){ if (x.right!=null){ return max(x.right); }else{ return x; } }
Basic traversal of binary trees
In many cases, we may need to traverse the tree, like an array, to take out every element stored in the tree. Because the tree structure is different from the linear structure, it cannot traverse backwards from the beginning, so there is a question of how to traverse, that is, what search path to follow.
By simply drawing the tree as shown in the figure above, which consists of a root node, a left subtree and a right subtree, we can divide the traversal of a binary tree into three ways according to when the root node is visited:
- Pre-order traversal; access the root node first, then the left subtree, and finally the right subtree
- Traverse in middle order; first access left subtree, middle access root node, and last access right subtree
- Later traversal; access left subtree, right subtree, and root node
If we traverse the following tree in three different ways, we get the following results
Pre-order traversal
Method | function |
---|---|
private void preErgodic(Node x,Queue keys) | Place all keys in the specified tree x in the keys queue using a prefix traversal |
public Queue preErgodic() | Use preorder traversal to get all keys in the entire tree |
Steps to achieve:
- Put the key of the current node in the queue;
- Find the left subtree of the current node, if not empty, recursively traverse the left subtree
- Find the right subtree of the current node, if not empty, recursively traverse the right subtree
Code
//Use preorder traversal to get all keys in the entire tree public Queue<Key> preErgodic(){ Queue<Key> keys = new Queue<>(); preErgodic(root,keys); return keys; } //Place all keys in the specified tree x in the keys queue using a prefix traversal private void preErgodic(Node x,Queue<Key> keys){ if (x==null){ return; } //1. Put the key of the current node in the queue; keys.enqueue(x.key); //2. Find the left subtree of the current node, if not empty, recursively traverse the left subtree if (x.left!=null){ preErgodic(x.left,keys); } //3. Find the right subtree of the current node, if not empty, recursively traverse the right subtree if (x.right!=null){ preErgodic(x.right,keys); } } //Test Code public class Test { public static void main(String[] args) throws Exception { BinaryTree<String, String> bt = new BinaryTree<>(); bt.put("E", "5"); bt.put("B", "2"); bt.put("G", "7"); bt.put("A", "1"); bt.put("D", "4"); bt.put("F", "6"); bt.put("H", "8"); bt.put("C", "3"); Queue<String> queue = bt.preErgodic(); for (String key : queue) { System.out.println(key+"="+bt.get(key)); } } }
Intermediate traversal
Method | function |
---|---|
public Queue midErgodic() | Use median traversal to get all keys in the entire tree |
private void midErgodic(Node x,Queue keys) | Place all keys in the specified tree x in the keys queue using intermediate traversal |
Steps to achieve:
- Find the left subtree of the current node, if not empty, recursively traverse the left subtree
- Put the key of the current node in the queue;
- Find the right subtree of the current node, if not empty, recursively traverse the right subtree
Code
//Use median traversal to get all keys in the entire tree public Queue<Key> midErgodic(){ Queue<Key> keys = new Queue<>(); midErgodic(root,keys); return keys; } //Place all keys in the specified tree x in the keys queue using intermediate traversal private void midErgodic(Node x,Queue<Key> keys){ if (x==null){ return; } //1. Find the left subtree of the current node, if not empty, recursively traverse the left subtree if (x.left!=null){ midErgodic(x.left,keys); } //2. Put the key of the current node in the queue; keys.enqueue(x.key); //3. Find the right subtree of the current node, if not empty, recursively traverse the right subtree if (x.right!=null){ midErgodic(x.right,keys); } } //Test Code public class Test { public static void main(String[] args) throws Exception { BinaryTree<String, String> bt = new BinaryTree<>(); bt.put("E", "5"); bt.put("B", "2"); bt.put("G", "7"); bt.put("A", "1"); bt.put("D", "4"); bt.put("F", "6"); bt.put("H", "8"); bt.put("C", "3"); Queue<String> queue = bt.midErgodic(); for (String key : queue) { System.out.println(key+"="+bt.get(key)); } } }
Post-order traversal
Method | function |
---|---|
public Queue afterErgodic() | Use post-order traversal to get all keys in the entire tree |
private void afterErgodic(Node x,Queue keys) | Place all keys in the specified tree x in the keys queue using postorder traversal |
Steps to achieve:
- Find the left subtree of the current node, if not empty, recursively traverse the left subtree
- Find the right subtree of the current node, if not empty, recursively traverse the right subtree
- Put the key of the current node in the queue;
Code
//Use post-order traversal to get all keys in the entire tree public Queue<Key> afterErgodic(){ Queue<Key> keys = new Queue<>(); afterErgodic(root,keys); return keys; } //Place all keys in the specified tree x in the keys queue using postorder traversal private void afterErgodic(Node x,Queue<Key> keys){ if (x==null){ return; } //1. Find the left subtree of the current node, if not empty, recursively traverse the left subtree if (x.left!=null){ afterErgodic(x.left,keys); } //2. Find the right subtree of the current node, if not empty, recursively traverse the right subtree if (x.right!=null){ afterErgodic(x.right,keys); } //3. Put the key of the current node in the queue; keys.enqueue(x.key); } //Test Code public class Test { public static void main(String[] args) throws Exception { BinaryTree<String, String> bt = new BinaryTree<>(); bt.put("E", "5"); bt.put("B", "2"); bt.put("G", "7"); bt.put("A", "1"); bt.put("D", "4"); bt.put("F", "6"); bt.put("H", "8"); bt.put("C", "3"); Queue<String> queue = bt.afterErgodic(); for (String key : queue) { System.out.println(key+"="+bt.get(key)); } } }