Javascript中二叉搜索树的实现
在本文中,我们将在 Javascript 中实现二叉搜索树数据结构。树是由一些边连接的节点的集合。树是一种非线性数据结构。二叉搜索树是一种二叉树,其中具有较小值的节点存储在左侧,而具有较高值的节点存储在右侧。
现在让我们看一个二叉搜索树节点的示例:
Javascript
// Node class
class Node
{
constructor(data)
{
this.data = data;
this.left = null;
this.right = null;
}
}
Javascript
// Binary Search tree class
class BinarySearchTree
{
constructor()
{
// root of a binary search tree
this.root = null;
}
// function to be implemented
// insert(data)
// remove(data)
// Helper function
// findMinNode()
// getRootNode()
// inorder(node)
// preorder(node)
// postorder(node)
// search(node, data)
}
Javascript
// helper method which creates a new node to
// be inserted and calls insertNode
insert(data)
{
// Creating a node and initialising
// with data
var newNode = new Node(data);
// root is null then node will
// be added to the tree and made root.
if(this.root === null)
this.root = newNode;
else
// find the correct position in the
// tree and add the node
this.insertNode(this.root, newNode);
}
// Method to insert a node in a tree
// it moves over the tree to find the location
// to insert a node with a given data
insertNode(node, newNode)
{
// if the data is less than the node
// data move left of the tree
if(newNode.data < node.data)
{
// if left is null insert node here
if(node.left === null)
node.left = newNode;
else
// if left is not null recur until
// null is found
this.insertNode(node.left, newNode);
}
// if the data is more than the node
// data move right of the tree
else
{
// if right is null insert node here
if(node.right === null)
node.right = newNode;
else
// if right is not null recur until
// null is found
this.insertNode(node.right,newNode);
}
}
Javascript
// helper method that calls the
// removeNode with a given data
remove(data)
{
// root is re-initialized with
// root of a modified tree.
this.root = this.removeNode(this.root, data);
}
// Method to remove node with a
// given data
// it recur over the tree to find the
// data and removes it
removeNode(node, key)
{
// if the root is null then tree is
// empty
if(node === null)
return null;
// if data to be delete is less than
// roots data then move to left subtree
else if(key < node.data)
{
node.left = this.removeNode(node.left, key);
return node;
}
// if data to be delete is greater than
// roots data then move to right subtree
else if(key > node.data)
{
node.right = this.removeNode(node.right, key);
return node;
}
// if data is similar to the root's data
// then delete this node
else
{
// deleting node with no children
if(node.left === null && node.right === null)
{
node = null;
return node;
}
// deleting node with one children
if(node.left === null)
{
node = node.right;
return node;
}
else if(node.right === null)
{
node = node.left;
return node;
}
// Deleting node with two children
// minimum node of the right subtree
// is stored in aux
var aux = this.findMinNode(node.right);
node.data = aux.data;
node.right = this.removeNode(node.right, aux.data);
return node;
}
}
Javascript
// Performs inorder traversal of a tree
inorder(node)
{
if(node !== null)
{
this.inorder(node.left);
console.log(node.data);
this.inorder(node.right);
}
}
Javascript
// Performs preorder traversal of a tree
preorder(node)
{
if(node !== null)
{
console.log(node.data);
this.preorder(node.left);
this.preorder(node.right);
}
}
Javascript
// Performs postorder traversal of a tree
postorder(node)
{
if(node !== null)
{
this.postorder(node.left);
this.postorder(node.right);
console.log(node.data);
}
}
Javascript
// finds the minimum node in tree
// searching starts from given node
findMinNode(node)
{
// if left of a node is null
// then it must be minimum node
if(node.left === null)
return node;
else
return this.findMinNode(node.left);
}
Javascript
// returns root of the tree
getRootNode()
{
return this.root;
}
Javascript
// search for a node with given data
search(node, data)
{
// if trees is empty return null
if(node === null)
return null;
// if data is less than node's data
// move left
else if(data < node.data)
return this.search(node.left, data);
// if data is less than node's data
// move left
else if(data > node.data)
return this.search(node.right, data);
// if data is equal to the node data
// return node
else
return node;
}
Javascript
// create an object for the BinarySearchTree
var BST = new BinarySearchTree();
// Inserting nodes to the BinarySearchTree
BST.insert(15);
BST.insert(25);
BST.insert(10);
BST.insert(7);
BST.insert(22);
BST.insert(17);
BST.insert(13);
BST.insert(5);
BST.insert(9);
BST.insert(27);
// 15
// / \
// 10 25
// / \ / \
// 7 13 22 27
// / \ /
// 5 9 17
var root = BST.getRootNode();
// prints 5 7 9 10 13 15 17 22 25 27
BST.inorder(root);
// Removing node with no children
BST.remove(5);
// 15
// / \
// 10 25
// / \ / \
// 7 13 22 27
// \ /
// 9 17
var root = BST.getRootNode();
// prints 7 9 10 13 15 17 22 25 27
BST.inorder(root);
// Removing node with one child
BST.remove(7);
// 15
// / \
// 10 25
// / \ / \
// 9 13 22 27
// /
// 17
var root = BST.getRootNode();
// prints 9 10 13 15 17 22 25 27
BST.inorder(root);
// Removing node with two children
BST.remove(15);
// 17
// / \
// 10 25
// / \ / \
// 9 13 22 27
var root = BST.getRootNode();
console.log("inorder traversal");
// prints 9 10 13 17 22 25 27
BST.inorder(root);
console.log("postorder traversal");
BST.postorder(root);
console.log("preorder traversal");
BST.preorder(root);
在上面的代码片段中,我们定义了一个节点类,它具有三个属性data , left和right , Left 和 right 是指向二叉搜索树中左右节点的指针。使用创建此节点的对象并将 left 和 right 设置为 null 时传递的数据初始化数据。
现在让我们看一个二叉搜索树类的示例。
Javascript
// Binary Search tree class
class BinarySearchTree
{
constructor()
{
// root of a binary search tree
this.root = null;
}
// function to be implemented
// insert(data)
// remove(data)
// Helper function
// findMinNode()
// getRootNode()
// inorder(node)
// preorder(node)
// postorder(node)
// search(node, data)
}
上面的例子展示了一个二叉搜索树类的框架,其中包含一个私有变量root保存树的根,它被初始化为 null。
现在让我们实现每个函数:
1. insert(data) - 它在树中插入一个新节点,其值为data
Javascript
// helper method which creates a new node to
// be inserted and calls insertNode
insert(data)
{
// Creating a node and initialising
// with data
var newNode = new Node(data);
// root is null then node will
// be added to the tree and made root.
if(this.root === null)
this.root = newNode;
else
// find the correct position in the
// tree and add the node
this.insertNode(this.root, newNode);
}
// Method to insert a node in a tree
// it moves over the tree to find the location
// to insert a node with a given data
insertNode(node, newNode)
{
// if the data is less than the node
// data move left of the tree
if(newNode.data < node.data)
{
// if left is null insert node here
if(node.left === null)
node.left = newNode;
else
// if left is not null recur until
// null is found
this.insertNode(node.left, newNode);
}
// if the data is more than the node
// data move right of the tree
else
{
// if right is null insert node here
if(node.right === null)
node.right = newNode;
else
// if right is not null recur until
// null is found
this.insertNode(node.right,newNode);
}
}
在上面的代码中,我们有两个方法insert(data)和insertNode(node, newNode) 。让我们一一了解:-
- insert(data) - 它创建一个具有值 data 的新节点,如果树为空,则将此节点添加到树中并使其成为根,否则调用insert(node, data) 。
- insert(node, data) - 它将给定的数据与当前节点的数据进行比较,并相应地向左或向右移动并循环,直到找到一个可以添加新节点的具有空值的正确节点。
2.remove(data) - 此函数删除具有给定数据的节点。
Javascript
// helper method that calls the
// removeNode with a given data
remove(data)
{
// root is re-initialized with
// root of a modified tree.
this.root = this.removeNode(this.root, data);
}
// Method to remove node with a
// given data
// it recur over the tree to find the
// data and removes it
removeNode(node, key)
{
// if the root is null then tree is
// empty
if(node === null)
return null;
// if data to be delete is less than
// roots data then move to left subtree
else if(key < node.data)
{
node.left = this.removeNode(node.left, key);
return node;
}
// if data to be delete is greater than
// roots data then move to right subtree
else if(key > node.data)
{
node.right = this.removeNode(node.right, key);
return node;
}
// if data is similar to the root's data
// then delete this node
else
{
// deleting node with no children
if(node.left === null && node.right === null)
{
node = null;
return node;
}
// deleting node with one children
if(node.left === null)
{
node = node.right;
return node;
}
else if(node.right === null)
{
node = node.left;
return node;
}
// Deleting node with two children
// minimum node of the right subtree
// is stored in aux
var aux = this.findMinNode(node.right);
node.data = aux.data;
node.right = this.removeNode(node.right, aux.data);
return node;
}
}
在上面的代码中,我们有两个方法remove(data)和removeNode(node, data) ,让我们一一理解:
- remove(data) - 它是通过传递根节点和给定数据调用 removeNode 的辅助方法,并使用函数返回的值更新树的根
- removeNode(node, data) - 它搜索具有给定数据的节点,然后执行某些步骤将其删除。
- 删除叶节点- 由于叶节点没有任何子节点,因此可以轻松删除它们并将 null 返回给父节点
- 删除一个有一个孩子的节点——如果一个节点有一个左孩子,那么我们将父节点的指针更新为要删除的节点的左孩子,类似地,如果一个节点有一个右孩子,那么我们更新指针要删除的节点的右子节点的父节点
- 删除一个有两个孩子的节点——为了删除一个有两个孩子的节点,我们在其右子树中找到具有最小值的节点,并将该节点替换为最小值节点,并从树中删除最小值节点
树遍历
现在让我们了解遍历二叉搜索树的不同方法。
inorder(node) - 它从给定节点开始执行树的中序遍历
中序算法:
Traverse the left subtree i.e perform inorder on left subtreeVisit the rootTraverse the right subtree i.e perform inorder on right subtree
Javascript
// Performs inorder traversal of a tree
inorder(node)
{
if(node !== null)
{
this.inorder(node.left);
console.log(node.data);
this.inorder(node.right);
}
}
1. preorder(node) - 它执行从给定节点开始的树的前序遍历。
预购算法:
Visit the rootTraverse the left subtree i.e perform preorder on left subtreeTraverse the right subtree i.e perform preorder on right subtree
Javascript
// Performs preorder traversal of a tree
preorder(node)
{
if(node !== null)
{
console.log(node.data);
this.preorder(node.left);
this.preorder(node.right);
}
}
2. postorder(node) - 它执行从给定节点开始的树的后序遍历。
后序算法:
Traverse the left subtree i.e perform postorder on left subtreeTraverse the right subtree i.e perform postorder on right subtreeVisit the root
Javascript
// Performs postorder traversal of a tree
postorder(node)
{
if(node !== null)
{
this.postorder(node.left);
this.postorder(node.right);
console.log(node.data);
}
}
辅助方法
让我们声明一些在使用二叉搜索树时很有用的辅助方法。
1. findMinNode(node) - 它从 node 开始搜索具有最小值的节点。
Javascript
// finds the minimum node in tree
// searching starts from given node
findMinNode(node)
{
// if left of a node is null
// then it must be minimum node
if(node.left === null)
return node;
else
return this.findMinNode(node.left);
}
从上面的方法中可以看出,我们从一个节点开始,一直移动到左子树,直到找到一个左子节点为空的节点,一旦找到这样的节点,我们就返回它。
2. getRootNode() – 返回树的根节点。
Javascript
// returns root of the tree
getRootNode()
{
return this.root;
}
3. search(data) - 它在整个树中搜索具有值数据的节点。
Javascript
// search for a node with given data
search(node, data)
{
// if trees is empty return null
if(node === null)
return null;
// if data is less than node's data
// move left
else if(data < node.data)
return this.search(node.left, data);
// if data is less than node's data
// move left
else if(data > node.data)
return this.search(node.right, data);
// if data is equal to the node data
// return node
else
return node;
}
注意:可以根据需要在BinarySearchTree类中声明不同的辅助方法。
执行
现在让我们使用BinarySearchTree类及其上面描述的不同方法。
Javascript
// create an object for the BinarySearchTree
var BST = new BinarySearchTree();
// Inserting nodes to the BinarySearchTree
BST.insert(15);
BST.insert(25);
BST.insert(10);
BST.insert(7);
BST.insert(22);
BST.insert(17);
BST.insert(13);
BST.insert(5);
BST.insert(9);
BST.insert(27);
// 15
// / \
// 10 25
// / \ / \
// 7 13 22 27
// / \ /
// 5 9 17
var root = BST.getRootNode();
// prints 5 7 9 10 13 15 17 22 25 27
BST.inorder(root);
// Removing node with no children
BST.remove(5);
// 15
// / \
// 10 25
// / \ / \
// 7 13 22 27
// \ /
// 9 17
var root = BST.getRootNode();
// prints 7 9 10 13 15 17 22 25 27
BST.inorder(root);
// Removing node with one child
BST.remove(7);
// 15
// / \
// 10 25
// / \ / \
// 9 13 22 27
// /
// 17
var root = BST.getRootNode();
// prints 9 10 13 15 17 22 25 27
BST.inorder(root);
// Removing node with two children
BST.remove(15);
// 17
// / \
// 10 25
// / \ / \
// 9 13 22 27
var root = BST.getRootNode();
console.log("inorder traversal");
// prints 9 10 13 17 22 25 27
BST.inorder(root);
console.log("postorder traversal");
BST.postorder(root);
console.log("preorder traversal");
BST.preorder(root);