Introduction to Binary Search Trees
- A 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 the right subtree exists, the value of each node in the right subtree is not less than the value of the node.
To 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 the value of the root node;
- If its right subtree is not empty, the values of all nodes on its right subtree are greater than the value of the root node;
- Its left and right subtrees should also be binary search trees respectively;
The process of querying nodes is to compare whether the element values are equal, and return if they are equal, and judge the size if they are not equal, and iteratively query the left and right subtrees until an equal element is 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 fill up each layer of the tree as much as possible, and if there are remaining nodes after the previous layer is filled, fill up the next layer as much as possible from left to right.
A binary tree with only one node at each level.
Node 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.1Node node instance
//Node节点实例
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)
Because the special nature of the binary search tree determines that each element in the binary search tree can only appear once, if it is found that this element already exists in the binary search tree during the insertion process, it will not be inserted. Otherwise, find a suitable location for insertion.
1.3.1 The first case: _root is empty
Insert 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 no longer be inserted, and return false directly; no longer let it be inserted;
function insertNode(node, newNode) {
if(newNode.key === node.key){
return false
}
}
Node insertion test
You can see that node 2 is not inserted repeatedly;
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: able to find a suitable location
function insertNode(node, newNode) {
if(newNode.key === node.key){
return false
}
if (newNode.key < node.key) {
// 如果新节点值小于当前节点值,则插入左子节点
if (node.left === null) {
node.left = newNode
} else {
insertNode(node.left, newNode)
}
} else {
// 如果新节点值小于当前节点值,则插入右子节点
if (node.right === null) {
node.right = newNode
} else {
insertNode(node.right, newNode)
}
}
}
1.3.4 Insert the full version of the node
//Node节点实例
function Node(key) {
this.key = key;
this.left = null;
this.right = null;
}
//二叉搜索树
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 (node.left === null) {
node.left = newNode
} else {
insertNode(node.left, newNode)
}
} else {
// 如果新节点值小于当前节点值,则插入右子节点
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 types
2.2 Preorder traversal
The so-called preorder traversal is to visit 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
accomplish
//先序遍历是以优先于后代节点的顺序访问每一个节点。
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 In-order traversal
The so-called in-order traversal is to visit the left node first, then the root node, and finally the right node.
Then the order is: 2->4->6->8->9->12->15
accomplish
//中序遍历是一种以从最小到最大的顺序访问所有节点的遍历方式
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 visit the left node first, then the right node, and finally the root node.
Then the order is: 2->6->4->9->15->12->8
accomplish
//后序遍历是先访问节点的后代节点,再访问节点本身
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 layer sequence traversal
The sequence traversal of the binary tree, that is, the breadth traversal of the binary tree, first traverses the adjacent nodes of the root node, and then traverses the child nodes of the adjacent nodes. Breadth traversal is usually achieved with the help of queues. Use the queue to store the number of nodes in 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, and enter the next layer to traverse until Traverse the complete tree, that is, complete the hierarchy traversal to the binary tree.
The picture is from the solution of : 161990d81318ea BFS usage scenario summary: sequence traversal, shortest path problem
Very exciting, like it! ! !
accomplish
let levelOrder = function (root) {
if (!root) return []
let res = [] //结果最外层数组
let queue = [root]
while (queue.length > 0) {
var len = queue.length//当前层的节点数目
var subRes = []
for (var i = 0; i < len; i++) {
var node = queue.shift() //节点出列
subRes.push(node.val) //将当前层的节点值加入subRes数组中
//将下一层节点计入队列中
if (node.left) {
queue.push(node.left)
}
if (node.right) {
queue.push(node.right)
}
}
res.push(subRes)
}
return res
};
Specifically, you can view the solution to the problem of : 161990d8131953 102. The sequence traversal of the 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 the binary search, passing in a value to determine whether it exists in the binary search tree.
3.1 Four situations
- First judge whether the passed node is null, if it is null, it means the search failed, and false is returned.
- If the node is found, return true.
- The node you are looking for is smaller than the current node, and continue searching on the left node.
- The node you are looking for is larger than the current node. Continue searching on the node on the right.
accomplish
let search = function (key) {
searchNode(this.roots, key)
}
function searchNode(node, key) {
//查找失败,返回 false
if (node === null) {
return false;
}
//比当前节点小,在左侧节点继续查找
if (node.key > key) {
searchNode(node.left, key)
}
//比当前节点大,在右侧节点继续查找
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.
accomplish
//最小值
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.
accomplish
//最大值
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. Binary tree node deletion
- First judge whether the node is null, and return directly if it is equal to null.
- Determine whether the node to be deleted is smaller than the current node, and search to the left of the tree
- Determine the node to be deleted is greater than the current node, look to the right side of the tree
The node has been found, and it is divided into four situations:
4.1. If the current node has no left node and no right node, 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 and right nodes are not empty
accomplish
//移除节点
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.要删除节点小于当前节点,往树的左侧查找
if (node.key > key) {
node.left = removeNode(node.left, key);
return node;
}
//2.要删除节点大于当前节点,往树的右侧查找
if (node.key < key) {
node.right = removeNode(node.right, key);
return node;
}
if (node.key === key) {
//1.当前节点即无左侧节点又无右侧节点,直接删除,返回 null
if (node.left === null && node.right === null) {
return null;
}
//2.若右侧节点为 null,就证明它有左侧节点,将当前节点的引用改为左侧节点的引用,返回更新之后的值
if (node.left !== null && node.right === null) {
return node.left;
}
//3.若左侧节点为 null,就证明它有右侧节点,将当前节点的引用改为右侧节点的引用,返回更新之后的值
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 for the complete code of the binary tree
//基础类
function BinarySearchTree() {
let Node = function (key) {
this.key = key;
this.left = null;
this.right = null;
}
this.roots = null;
//二叉树插入
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 (node.left === null) {
node.left = newNode
} else {
insertNode(node.left, newNode)
}
} else {
// 如果新节点值小于当前节点值,则插入右子节点
if (node.right === null) {
node.right = newNode
} else {
insertNode(node.right, newNode)
}
}
}
//中序遍历是一种以从最小到最大的顺序访问所有节点的遍历方式
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)
}
}
//先序遍历是以优先于后代节点的顺序访问每一个节点。
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)
}
}
//后序遍历是先访问节点的后代节点,再访问节点本身
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)
}
}
//搜索二叉树
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;
}
//最小值
this.min = function () {
minNode(this.roots)
}
function minNode(node) {
while (node !== null && node.left !== null) {
node = node.left
}
return node.key
}
//最大值
this.max = function () {
maxNode(this.roots)
}
function maxNode(node) {
while (node !== null && node.right !== null) {
node = node.right
}
return node.key
}
//移除节点
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.要删除节点小于当前节点,往树的左侧查找
if (node.key > key) {
node.left = removeNode(node.left, key);
return node;
}
//2.要删除节点大于当前节点,往树的右侧查找
if (node.key < key) {
node.right = removeNode(node.right, key);
return node;
}
if (node.key === key) {
//1.当前节点即无左侧节点又无右侧节点,直接删除,返回 null
if (node.left === null && node.right === null) {
return null;
}
//2.若右侧节点为 null,就证明它有左侧节点,将当前节点的引用改为左侧节点的引用,返回更新之后的值
if (node.left !== null && node.right === null) {
return node.left;
}
//3.若左侧节点为 null,就证明它有右侧节点,将当前节点的引用改为右侧节点的引用,返回更新之后的值
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
For the same data, different insertion order, the result of the tree is different. This is the limitation of the binary tree. At the same time, when there are too many nodes, it will consume more performance. If you have any questions, you can leave a message in the comment area, learn together, and work hard together. You can also visit the personal blog address. Call me Zhan
Online DEMO address Online DEMO address
9. Reference materials
1. Javascript implementation of binary tree search and node deletion
2. Javascript implementation of binary tree
JavaScript binary tree in-depth understanding
4. Data structure (two): Binary Search Tree
5. Implement a binary search tree (JavaScript version)
6. Binary search tree insertion and deletion diagram
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。