参考:《数据结构与算法 JavaScript 描述》
本篇文章中,给二叉查找树,添加四个数据操作方法,这四个方法也展示了二叉查找树查找数据的快捷性。
在二叉查找树上进行查找
对 BST 通常有下列三种类型的查找:
(1) 查找给定值;
(2) 查找最小值;
(3) 查找最大值。
查找 BST 上的最小值和最大值非常简单。因为较小的值总是在左子节点上,在 BST 上查找最小值,只需要遍历左子树,直到找到最后一个节点。查找最大值也是同样的道理。
首先给我们的BST构造函数添上新的方法:
function BST() {
this.root = null; //初始根节点定义为null
this.insert = insert; //插入节点方法
this.inOrder = inOrder; //中序遍历方法
this.getMinVal = getMinVal; //查找最小值方法
this.getMaxVal = getMaxVal; //查找最大值方法
this.find = find; //查找给定值方法
}
实现getMin()、getMax() 方法
查找最小值的思路为,沿着 BST 的左子树挨个遍历,直到遍历到 BST 最左边的节点,这个节点满足:
node.left = null;
方法定义如下:
//查找最小值
function getMinVal(node) {
var current = node.root;
while(!(current.left == null)){
current = current.left;
}
return current;
}
在 BST 上查找最大值,只需要遍历右子树,直到找到最后一个节点,该节点上保存的值即为最大值。
方法定义如下:
//查找最大值
function getMaxVal(node) {
var current = node.root;
while(!(current.right == null)){
current = current.right;
}
return current;
}
实现查找给定值方法
在 BST 上查找给定值,需要比较该值和当前节点上的值的大小。通过比较,就能确定如果给定值不在当前节点时,该向左遍历还是向右遍历。
该方法定义如下:
//查找给定值,如找到给定值,返回该值的节点;如果没找到,返回null。
function find(data) {
var current = this.root;
while(!(current == null)){
if (current.data == data){
return current;
}else if(data < current.data){
current = current.left;
}else{
current = current.right;
}
}
return null;
}
从二叉查找树上删除节点
从 BST 上删除节点的操作最复杂,其复杂程度取决于删除哪个节点。如果删除没有子节点的节点,那么非常简单。如果节点只有一个子节点,不管是左子节点还是右子节点,就变得稍微有点复杂了。删除包含两个子节点的节点最复杂。
为了管理删除操作的复杂度,我们使用递归操作,同时定义两个方法:remove() 和removeNode()。
从 BST 中删除节点的第一步是判断当前节点是否包含待删除的数据,如果包含,则删除该节点;如果不包含,则比较当前节点上的数据和待删除的数据。如果待删除数据小于当前节点上的数据,则移至当前节点的左子节点继续比较;如果删除数据大于当前节点上的数据,则移至当前节点的右子节点继续比较。
如果待删除节点是叶子节点(没有子节点的节点),那么只需要将从父节点指向它的链接指向 null。
如果待删除节点只包含一个子节点,那么原本指向它的节点久得做些调整,使其指向它的子节点。
最后,如果待删除节点包含两个子节点,正确的做法有两种:要么查找待删除节点左子树上的最大值,要么查找其右子树上的最小值。这里我们选择后一种方式。
整个删除过程由三个方法完成。remove() 方法只是简单地接受待删除数据,调用 removeNode()删除它,后者才是完成主要工作的方法,getSmallest() 方法,用于查找指定节点最小值。三个方法的定义如下:
/*
从二叉查找树上删除节点,为了管理删除操作的复杂度,使用递归操作,
同时定义两个方法:remove()和removeNode()。
*/
function remove(data) {
// console.log(this);
removeNode(this.root,data);
}
function removeNode(node,data) {
//容错处理
if(node == null){
return null;
}
//主处理
if(data == node.data){
//没有子节点的节点
if(node.left == null && node.right == null){
return null;}
//没有左子节点的节点
if(node.left == null){
return node.right;}
//没有右子节点的节点
if(node.right == null){
return node.left;}
//有两个子节点的节点
//寻找右子树的最小值,将其立为新的被删节点的键值,
//然后对右子树的所有右子树递归执行removeNode,一层一层的往上移
var tempNode = getSmallest(node.right);
node.data = tempNode.data;
node.right = removeNode(node.right,tempNode.data);
return node;
}else if(data < node.data){
node.left = removeNode(node.left,data);
return node;
}else{
node.right = removeNode(node.right,data);
return node;}}
function getSmallest(node){
while (node.left != null){
node = node.left;
}
return node;
}
删除节点操作的本质就是改变节点的data值,这里是将右子树最小值,赋予给待删除节点,然后在右子树中寻找这个最小值,把它给删掉,怎么删呢,这里只有两种情况:
第一种,最小值节点无子节点,就直接赋为null。
第二种,最小值节点有右节点,就直接将右节点的data值赋给它。
到这里,一个二叉查找树的所应该有基本功能,就都实现了!