理论
二叉查找树,也就是二叉排序树或者二叉搜索树,它是二叉树的一种分类。它突出的特性是,每个节点的左子节点始终比该节点小,每个节点的右子节点始终比该节点大。这是说,任意节点的左子树上的所有结点,永远比该节点的右子树上的所有结点的值小。同时,它的任意左、右子树,也各自是二叉查找树。
代码
以下是使用js代码来实现。
先定义二叉查找树的节点:
/**
* 树的节点
* @param data 节点的数值
* @param left 该节点的左子节点
* @param right 该节点的右子节点
*/
创建一个名为TreeNode的函数,该函数带有三个参数,分别是数据,左结点,右结点 。
this.data = data;
this.left = left || null;
this.right = right || null;
}
定义二叉树:
/**
* 二叉查找树
* @param rootNode 根节点
*/
用于定义一个函数,这个函数名为BinarySearchTree,它接收一个参数rootNode ,。
这项,这个的根部节点,等于跟部节点或者为空,为空。
}
用于二叉搜索树的属性原型被定义为,这样一种形式,其内容为。
// 后面讲的查找,新增,删除功能会放在本区域(即原型)
}
二叉查找树的新增功能
插入工作必须按照二叉查找树的特性来完成。
首先,判断那个二叉树是不是有根节点,要是没有的话,那就把新节点当作根节点,反过来要是有的话,那就接着往下;从根节点开始,一级一级去比较,也就是说呢,如果新结点的值比根节点的值小,那么就将新结点跟根节点左子树之中的节点相比较,要是大的话,那就跟根节点右子树之中的节点相比较;一级一级比较,一直到找到需要插入的空位,然后把新节点放进去就行;。
新增代码如下:
/**
* 插入节点
* @param data 需要插入节点的值
*/
function insert(data) {
// 生成新的节点
变量“newNode”等于通过“TreeNode”这个东西,使用“data”创建出来的新节点 。
// 判断根节点是否存在
if (!this.rootNode) {
this.rootNode = newNode;
return;
}
声明一个变量,名为currentNode,将其赋值为,this.rootNode 。
var parent = null;
while (true) {
parent = currentNode;
if (data < currentNode.data) {
currentNode = currentNode.left;
if (!currentNode) {
parent.left = newNode;
return;
}
} else if (data > currentNode.data) {
if (!currentNode) {
parent.right = newNode;
return;
}
} else {
console.log("该节点已存在");
return;
}
}
}
二叉查找树的查找功能
查找功能实际上跟新增功能相类似,首先得判断根节点是不是存在,接着要把待查找的节点从根节点起始,逐级开展比较查找,规则同样是左子树的节点始终小于右子树的节点,直至找到才终止。
/**
* 查找节点
* @param data 待查找节点的值
*/
function: find(data) {
// 判断根节点是否存在
if (!this.rootNode) {
return;
}
当前节点被设定为,这个根节点,它是this.rootNode。
while (true) {
if (!currentNode) {
console.log("该节点不存在");
return;
}
if (data < currentNode.data) {
currentNode = currentNode.left;
} else if (data > currentNode.data) {
当前节点 = 当前节点的右侧节点,此时当前节点被赋予右侧节点的值 ,从而实现当前节点的切换 。
} else {
return currentNode;
}
}
}
二叉查找树的节点删除功能
删除功能就比较复杂了,主要分三种情况:
当待删除节点不存在子节点的时候,直接将该节点删除便可;当待删除节点仅仅存在右子树或者仅仅存在左子树的时候,直接把待删除节点父节点的指针指向待删除节点的右子树或者左子树就行,之后删除节点;而这种情形相对而言比较复杂,也就是待删除节点既存在左节点又存在右节点。通常存在两种方式来予以解决:
进行这样的操作,要从待删除节点当下所在的左子树之中,选取出其中最大的那个节点,也就是最靠近右边方向的那个节点,让其去顶替因为删除操作之后而空出来的节点位置;还要从待删除节点当下所在的右子树之中,选取出其里面最小的那个节点,也就是最靠近左边方位的那个节点,使其去顶替因删除而腾出的节点位置。
具体流程如下:
1. 查找到待删除的结点和它的父节点;
2. 判断该节点有无左右子树,如果没有,那么直接删除;
假设存在这样一种情况,当仅有左右子树当中的一个时,那么便直接将它的父节点指向它的左子树或者右子树,随后进行删除节点的操作。
4. 要是左右两侧的子树都存在,那本人依照从左子树那儿选取最大节点的方式来进行实施,。
/**
* 删除目标节点
* @param data 待删除目标节点的值
*/
function removeNode(data) {
// 判断根节点是否存在
if (!this.rootNode) {
return;
}
// 目标节点的父节点
var parent = null;
// 目标节点
var currentNode = this.rootNode;
// 目标节点位于父节点的位置
var place = null;
while (true) {
if (!currentNode) {
console.log("该节点不存在");
return;
}
if (data < currentNode.data) {
parent = currentNode;
currentNode = currentNode.left;
place = "left";
} else if (data > currentNode.data) {
parent = currentNode;
currentNode = currentNode.right;
place = "right";
} else {
// 找到对应节点跳出循环
break;
}
}
if (!currentNode.left) {
// 删除的节点没有左孩子的情况
父亲(场所)等于当前节点的右边或者为空,这里表述有些奇怪,不太对应原英文逻辑准确改写,原英文意思大概是:将父 [某个地方] 设置为当前节点的右节点或者为空值,。
如果不是这样,要是当前节点的右边部分不存在的话,那么。
// 删除的节点没有右孩子的情况
让父节点的指定位置,等于当前节点的左子节点,要是没有左子节点那就等于空值 。
} else {
// 用于代替的节点
首先,有一个变量,它被命名为replaceNode,接着,把currentNode这个节点的left属性所对应的节点赋值给它,句号。
// 代替节点的父节点
var replaceNodeParent = null;
// 循环找出左子树中最大的节点(即用于代替的节点)
while(replaceNode.right) {
有一个名为replaceNodeParent的东西,它获取了replaceNode这个对象,然后让replaceNodeParent等于replaceNode 。
右移替换节点,将替换节点设为其右节点,此时替换节点等于替换节点的右节点,这样替换节点就变为了替换节点的右节点 。
}
// 代替原位置
parent[place] = replaceNode;
// 当代替节点就是删除的节点的左子节点时无需赋左子树
if (replaceNodeParent) {
若替换节点的左节点存在,那么替换节点父节点的右节点就等于该替换节点的左节点,要是替换节点的左节点不存在,那么替换节点父节点的右节点就等于空值 。
将当前节点左侧的值,赋给位于特定位置(作为父节点的那个位置)的左侧属性 ,句号。
}
// 获取删除的节点的右子树
当前节点的右边部分,被设定为,父节点在特定位置处的右边部分 。用当前节点右边部分的值,替换父节点处在特定位置时右边部分的值 。
}
return;
}