Introduction to binary search tree
- Binary search tree is a binary tree with a certain order of magnitude between node values. For each node in the tree:
- If its left subtree exists, the value of each node in its left subtree is not greater than the value of the node;
- If its right subtree exists, the value of each node in its right subtree is not less than the node value.
Meet the conditions
- If the left subtree is not empty, the values of the left and right nodes on the left subtree are less than those of the root node;
- If its right subtree is not empty, the values of all nodes on its right subtree are greater than those of the root node;
- Its left and right subtrees should also be binary search trees respectively;
The node query process is to compare whether the element values are equal, return if they are equal, and judge the size if they are not equal. Iterate to query the left and right subtrees until equal elements are found or the child node is empty, and the returned node does not exist.
Two special binary trees
For a complete binary tree, all nodes shall fill each layer of the tree as much as possible. If there are still remaining nodes after the previous layer is filled, the next layer shall be filled from left to right as much as possible.A binary tree with only one node in each layer.Node instance
let Node = function (key) { this.key = key; this.left = null; this.right = null; } this.roots = null;
Instance a node
let node = new Node() console.log(node) //{ key: undefined, left: null, right: null }
1. Binary tree insertion
1.1 node instance
//Node instance function Node(key) { this.key = key; this.left = null; this.right = null; }
1.2 binary tree object
function BinarySearchTree() { this.roots = null; this.insert = insert }
1.3 node insertion (three cases)
Due to the special nature of the binary search tree, each element in the binary search tree can only appear once. Therefore, in the process of insertion, if it is found that this element already exists in the binary search tree, it will not be inserted. Otherwise, find a suitable location for insertion.
1.3.1 the first case:_ root is emptyInsert directly, return true;
let insert = function (key) { let newNode = new Node(key) if (this.roots === null) { this.roots = newNode } }1.3.2 the second case: the element to be inserted already exists
As mentioned above, if the element already exists in the binary search tree, it will not be inserted. Return false directly; No longer let it insert;
function insertNode(node, newNode) { if(newNode.key === node.key){ return false } }Node insertion test
It can be seen that node 2 is not repeatedly inserted;
let BST = new BinarySearchTree(); BST.insert(2) BST.insert(2) BST.insert(7) BST.insert(3) BST.insert(1) console.log(BST)1.3.3 the third case: the right position can be found
function insertNode(node, newNode) { if(newNode.key === node.key){ return false } if (newNode.key < node.key) { // If the new node value is less than the current node value, the left child node is inserted if (node.left === null) { node.left = newNode } else { insertNode(node.left, newNode) } } else { // If the new node value is less than the current node value, the right child node is inserted if (node.right === null) { node.right = newNode } else { insertNode(node.right, newNode) } } }1.3.4 node insertion full version
//Node instance function Node(key) { this.key = key; this.left = null; this.right = null; } //Binary search tree function BinarySearchTree() { this.roots = null; this.insert = insert } let insert = function (key) { let newNode = new Node(key) if (this.roots === null) { this.roots = newNode } else { insertNode(this.roots, newNode) } } function insertNode(node, newNode) { if(newNode.key === node.key){ return false } if (newNode.key < node.key) { // If the new node value is less than the current node value, the left child node is inserted if (node.left === null) { node.left = newNode } else { insertNode(node.left, newNode) } } else { // If the new node value is less than the current node value, the right child node is inserted if (node.right === null) { node.right = newNode } else { insertNode(node.right, newNode) } } } let BST = new BinarySearchTree(); BST.insert(2) BST.insert(6) BST.insert(7) BST.insert(3) BST.insert(1) console.log(BST)
2. Binary tree traversal (four types)
2.1 binary tree traversal type
2.2 preorder traversal
The so-called preorder traversal is to access the root node first, then the left node, and finally the right node.
Then the traversal order is: 8 - > 4 - > 2 - > 6 - > 12 - > 9 - > 15
realization//Preorder traversal is to access each node in the order that takes precedence over the descendant nodes. let preOrderTraverse = function (callback) { preOrderTraverseNode(this.roots, callback) } function preOrderTraverseNode(node, callback) { if (node!==null) { callback(node.key) preOrderTraverseNode(node.left, callback) preOrderTraverseNode(node.right, callback) } } let BST = new BinarySearchTree(); let tree =[8,4,2,6,12,9,15] tree.forEach(v=>{ BST.insert(v) }) BST.preOrderTraverse(key=>console.log(key)) //8,4,2,6,12,9,15
2.3 middle order traversal
The so-called middle order traversal is to access the left node first, then the root node, and finally the right node.
Then the order is: 2 - > 4 - > 6 - > 8 - > 9 - > 12 - > 15
realization//Medium order traversal is a traversal method that accesses all nodes in order from minimum to maximum let inOrderTraverse = function (callback) { inOrderTraverseNode(this.roots, callback) } function inOrderTraverseNode(node, callback) { if (node !== null) { inOrderTraverseNode(node.left, callback) callback(node.key) inOrderTraverseNode(node.right, callback) } } let BST = new BinarySearchTree(); let tree = [8, 4, 2, 6, 12, 9, 15] tree.forEach(v => { BST.insert(v) }) BST.inOrderTraverse(key => console.log(key)) //2->4->6->8->9->12->15
2.4 post order traversal
The so-called post order traversal is to access the left node first, then the right node, and finally the root node.
Then the order is: 2 - > 6 - > 4 - > 9 - > 15 - > 12 - > 8
//Postorder traversal is to access the descendant nodes of the node first, and then access the node itself let postOrderTraverse = function (callback) { postOrderTraverseNode(this.roots, callback) } function postOrderTraverseNode(node, callback) { if (node!==null) { postOrderTraverseNode(node.left, callback) postOrderTraverseNode(node.right, callback) callback(node.key) } }
2.5 sequence traversal
The sequence traversal of binary tree, that is, the breadth traversal of binary tree, first traverses the adjacent nodes of the root node, and then traverses the child nodes of the adjacent nodes again. Breadth traversal is usually implemented by means of queues. Use the queue to store the number of nodes of the current layer, traverse the nodes of the current layer, push the nodes of the current layer into the array subRes [], and then push the left and right child nodes of the current node into the queue to enter the next layer for traversal until the complete tree is traversed, that is, the sequence traversal to the binary tree is completed.
Picture from leetcode: Summary of BFS usage scenarios: sequence traversal and shortest path problem
Very wonderful, like!!!
realizationlet levelOrder = function (root) { if (!root) return [] let res = [] //Result outermost array let queue = [root] while (queue.length > 0) { var len = queue.length//Number of nodes in the current layer var subRes = [] for (var i = 0; i < len; i++) { var node = queue.shift() //Node dequeue subRes.push(node.val) //Adds the node value of the current layer to the subRes array //Queue the next level nodes if (node.left) { queue.push(node.left) } if (node.right) { queue.push(node.right) } } res.push(subRes) } return res };
For details, see the solution of leetcode: 102. Sequence traversal of binary tree
3 binary tree search
In JavaScript, we can use hasOwnProperty to detect whether the specified key exists in the object. Now we implement a similar method in binary search, passing in a value to judge whether it exists in the binary search tree.
3.1 four cases
- First judge whether the incoming node is null. If it is null, it means that the search fails and returns false.
- If the node is found, return true.
- The node to be found is smaller than the current node. Continue to find the node on the left.
- The node to be found is larger than the current node. Continue to find the node on the right.
let search = function (key) { searchNode(this.roots, key) } function searchNode(node, key) { //Search failed, return false if (node === null) { return false; } //Smaller than the current node, continue to find the node on the left if (node.key > key) { searchNode(node.left, key) } //Larger than the current node, continue to find the node on the right if (node.key < key) { searchNode(node.right, key) } return true; }
4. Find the smallest node
Find the minimum value and search to the left of the binary tree until the node left is null and there is no left node, which proves that it is the minimum value.
realization
//minimum value function min() { return minNode(this.roots) || null } function minNode(node) { console.log(node) while (node !== null && node.left !== null) { node = node.left } return node.key } let BST = new BinarySearchTree(); let tree = [8, 4, 2, 6, 12, 9, 15] tree.forEach(v => { BST.insert(v) }) console.log(BST.min()) //2
5. Find the largest node
Find the maximum value and search to the right side of the binary tree until the node right is null and there is no right node, which proves that it is the maximum value.
realization
//Maximum let max = function () { return maxNode(this.roots) || null } function maxNode(node) { while (node !== null && node.right !== null) { node = node.right } return node.key } let BST = new BinarySearchTree(); let tree = [8, 4, 2, 6, 12, 9, 15] tree.forEach(v => { BST.insert(v) }) console.log(BST.max()) //15
6. Delete binary tree node
- First judge whether the node is null. If it is equal to null, it will be returned directly.
- Judge that the node to be deleted is smaller than the current node, and find it on the left side of the tree
- Judge that the node to be deleted is larger than the current node, and find it on the right side of the tree
The node has been found, which can be divided into four cases:
4.1. The current node has neither left nor right nodes. Delete it directly and return null
4.2. If the left node is null, it proves that it has a right node. Change the reference of the current node to the reference of the right node and return the updated value
4.3. If the right node is null, it proves that it has a left node. Change the reference of the current node to the reference of the left node and return the updated value
4.4. If the left node and the right node are not empty
realization
//Remove node let remove = function (key) { this.roots = removeNode(this.roots, key) console.log(this.roots) } function findMinNode(node, key) { while (node !== null && node.left !== null) { node = findMinNode(node.left, key); } return node; } function removeNode(node, key) { //1. To delete a node smaller than the current node, look to the left side of the tree if (node.key > key) { node.left = removeNode(node.left, key); return node; } //2. To delete a node larger than the current node, look it up on the right side of the tree if (node.key < key) { node.right = removeNode(node.right, key); return node; } if (node.key === key) { //1. The current node has neither left nor right nodes. Delete it directly and return null if (node.left === null && node.right === null) { return null; } //2. If the right node is null, it proves that it has a left node. Change the reference of the current node to the reference of the left node and return the updated value if (node.left !== null && node.right === null) { return node.left; } //3. If the left node is null, it proves that it has a right node. Change the reference of the current node to the reference of the right node and return the updated value if (node.left === null && node.right !== null) { return node.right; } node.right = findMinNode(node.right, key); return node; } } let BST = new BinarySearchTree(); let tree = [8, 4, 2, 6, 12, 9, 15] tree.forEach(v => { BST.insert(v) }) console.log(BST.remove(15)) //15
7. Search the complete code of binary tree
//Basic class function BinarySearchTree() { let Node = function (key) { this.key = key; this.left = null; this.right = null; } this.roots = null; //Binary tree insertion this.insert = function (key) { let newNode = new Node(key) if (this.roots === null) { this.roots = newNode } else { insertNode(this.roots, newNode) } } function insertNode(node, newNode) { if (newNode.key < node.key) { // If the new node value is less than the current node value, the left child node is inserted if (node.left === null) { node.left = newNode } else { insertNode(node.left, newNode) } } else { // If the new node value is less than the current node value, the right child node is inserted if (node.right === null) { node.right = newNode } else { insertNode(node.right, newNode) } } } //Medium order traversal is a traversal method that accesses all nodes in order from minimum to maximum this.inOrderTraverse = function (callback) { inOrderTraverseNode(this.roots, callback) } function inOrderTraverseNode(node, callback) { if (node !== null) { inOrderTraverseNode(node.left, callback) callback(node.key) inOrderTraverseNode(node.right, callback) } } //Preorder traversal is to access each node in the order that takes precedence over the descendant nodes. this.preOrderTraverse = function (callback) { preOrderTraverseNode(this.roots, callback) } function preOrderTraverseNode(node, callback) { if (node !== null) { callback(node.key) preOrderTraverseNode(node.left, callback) preOrderTraverseNode(node.right, callback) } } //Postorder traversal is to access the descendant nodes of the node first, and then access the node itself this.postOrderTraverse = function (callback) { postOrderTraverseNode(this.roots, callback) } function postOrderTraverseNode(node, callback) { if (node !== null) { postOrderTraverseNode(node.left, callback) postOrderTraverseNode(node.right, callback) callback(node.key) } } //Search Binary Tree this.search = function (key) { searchNode(this.roots, key) } function searchNode(node, key) { if (node === null) { return false; } if (node.key > key) { searchNode(node.left, key) } if (node.key < key) { searchNode(node.right, key) } return true; } //minimum value this.min = function () { minNode(this.roots) } function minNode(node) { while (node !== null && node.left !== null) { node = node.left } return node.key } //Maximum this.max = function () { maxNode(this.roots) } function maxNode(node) { while (node !== null && node.right !== null) { node = node.right } return node.key } //Remove node this.remove = function (key) { this.roots = removeNode(this.roots, key) } function findMinNode(node, key) { while (node !== null && node.left !== null) { node = findMinNode(node.left, key); } return node; } function removeNode(node, key) { //1. To delete a node smaller than the current node, look to the left side of the tree if (node.key > key) { node.left = removeNode(node.left, key); return node; } //2. To delete a node larger than the current node, look it up on the right side of the tree if (node.key < key) { node.right = removeNode(node.right, key); return node; } if (node.key === key) { //1. The current node has neither left nor right nodes. Delete it directly and return null if (node.left === null && node.right === null) { return null; } //2. If the right node is null, it proves that it has a left node. Change the reference of the current node to the reference of the left node and return the updated value if (node.left !== null && node.right === null) { return node.left; } //3. If the left node is null, it proves that it has a right node. Change the reference of the current node to the reference of the right node and return the updated value if (node.left === null && node.right !== null) { return node.right; } node.right = findMinNode(node.right, key); return node; } } } let nodeTree = [1, 12, 2, 3, 4, 5, 14, 6, 19]; let BST = new BinarySearchTree(); nodeTree.forEach(v => { BST.insert(v) }) console.log(BST.remove(19)) console.log(BST.max()) console.log(BST.min()) console.log(BST.preOrderTraverse(key => console.log(key))) console.log(BST.inOrderTraverse(key => console.log(key))) console.log(BST.postOrderTraverse(key => console.log(key)))
8. Summary
The results of the tree are different for different data and different insertion order. This is the limitation of binary tree. At the same time, when there are too many nodes, the performance consumption will be compared. If you have any questions, you can leave a message in the comment area, study together and improve together. You can also visit your personal blog address. Tell me Zhan to hide
Online DEMO address Online DEMO address
9. References
1. javascript implementation of binary tree search and node deletion
2. javascript implementation of binary tree
3. In depth understanding of JavaScript binary tree
4. Data structure (II): Binary Search Tree
5. Implement a binary search tree (JavaScript version)
6. Insertion and deletion diagram of binary search tree