二叉搜索树最麻烦的地方就是删除结点了,具体步骤如图。

function BinarySearchTree() {
// 定义BST实例的属性
this.root = null;
this.count = 0;
// 定义构造结点的函数
function Node(key) {
this.key = key;
this.left = null;
this.right = null;
}
// 插入
BinarySearchTree.prototype.insert = function (key) {
var newNode = new Node(key);
if (this.root == null) {
this.root = newNode;
}
else {
insertNode(this.root, newNode);
}
this.count++;
// 递归插入
function insertNode(node, newNode) {
// 向左递归
if (node.key > newNode.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);
}
}
}
}
// 前序遍历
BinarySearchTree.prototype.preOrderTraverse = function () {
var str = "";
pre(this.root)
// 递归
function pre(node) {
if (node == null) {
return;
}
else {
str += node.key + " " // 根
pre(node.left); // 左
pre(node.right); // 右
}
}
console.log(str);
}
// 中序遍历
BinarySearchTree.prototype.midOrderTraverse = function () {
var str = "";
mid(this.root)
// 递归
function mid(node) {
if (node == null) {
return;
}
else {
mid(node.left); // 左
str += node.key + " " // 根
mid(node.right); // 右
}
}
console.log(str);
}
// 后序遍历
BinarySearchTree.prototype.postOrderTraverse = function () {
var str = "";
post(this.root)
// 递归
function post(node) {
if (node == null) {
return;
}
else {
post(node.left); // 左
post(node.right); // 右
str += node.key + " " // 根
}
}
console.log(str);
}
// 最小值
BinarySearchTree.prototype.getMin = function () {
return left(this.root);
// 利用二叉搜索树的特性,进行递归查找左结点即为最小值
function left(node) {
var key;
if (node == null) {
return key;
}
else if(node.left = null) {
key = left(node.left);
}
else {
key = node.key;
}
return key;
}
}
// 最大值
BinarySearchTree.prototype.getMax = function () {
return right(this.root);
// 利用二叉搜索树的特性,进行递归查找右结点即为最大值
function right(node) {
var key;
if(node == null) {
}
else if(node.right != null) {
key = right(node.right);
}
else {
key = node.key;
}
return key;
}
}
// 根据key查找结点, 返回node,找不到则返回undefined;
BinarySearchTree.prototype.search = function(key) {
return search(this.root,key);
// 利用二叉搜索树的特性,进行递归查找
function search(node,key) {
var result ;
if(node === null) {
}
else if(node.key == key){
result = node;
}
else {
// 向左查找
if(key < node.key ) {
result = search(node.left,key);
}
// 向右查找
else {
result = search(node.right,key);
}
}
return result;
}
}
BinarySearchTree.prototype.reMove = function(key) {
//第一步:找到要删除的结点,其中要保留父节点,当前结点,以及标记该结点是父结点的左子还是右子结点。
var parentNode = null; //保留父节点
var current = this.root; //当前结点
var isLeftChild = true; //标记删除结点是父结点的左子还是右子结点
while(current.key!=key && current != null) {
parentNode = current; //保留父节点
//判断往左 还是往右
if(current.key > key) {
current = current.left;
isLeftChild = true;
}
else {
current = current.right;
isLeftChild = false;
}
}
// 说明未找到。
if(current == null) return false;
console.log("当前结点:"+current.key)
//第二步:根据找到的结点 进行不同情况的删除。
// 1 叶节点的情况,我们只需要把叶节点设置成null即可
if(current.left == null && current.right == null) {
console.log("删除叶结点")
// 1.1该节点为根结点
if(current == this.root) {
this.root = null;
}
// 1.2该节点不为根结点
else{
isLeftChild ? parentNode.left=null:parentNode.right=null;
}
}
// 2 节点且只有一个子节点的情况 我们只需要把父节点指向子节点即可
// 2.1只有左子结点
else if (current.left != null && current.right == null) {
console.log("删除只有1个子结点的结点")
//判断是否为为根结点
if(current == this.root) {
this.root = current.left;
}
else {
isLeftChild ? parentNode.left = current.left : parentNode.right = current.left;
}
}
// 2.2只有右子结点
else if(current.left == null && current.right != null) {
console.log("删除只有1个子结点的结点")
//判断是否为为根结点
if(current == this.root) {
this.root = current.right;
}
else {
isLeftChild ? parentNode.left = current.right : parentNode.right = current.right;
}
}
// 3 有2个子节点的情况 我们要根据替换的规律 既是在左子树中找最大的值(前驱) 或者 在右子树中找最小的值(后继) 然后与该结点替换。再将给结点设置为null
else {
console.log("删除有2个子结点的结点")
// 3.2.1定义前驱结点,前驱结点的父结点
var frontNode = current.left; // 前驱结点
var frontParentNode = current; // 前驱结点的父结点
// 3.2.2循环找前驱结点,前驱结点的父结点
while(frontNode.right!=null) {
frontParentNode = frontNode;
frontNode = frontNode.right;
}
console.log("前驱结点"+frontNode.key,"前驱结点的父结点"+frontParentNode.key);
// 3.2.3开始删除结点
// 3.2.3.1因为直接找的是前驱结点,所以前驱结点的右孩子直接指向删除结点的右孩子即可。
frontNode.right = current.right;
// 3.2.3.2判断前驱结点是删除结点的子代还是隔代。
// 如果是隔代 在考虑两种情况下把删除结点的左子树赋值给前驱结点。
if(frontParentNode != current ) {
//第一种情况,如果前驱结点为叶子结点,那么前驱结点的左孩子指向删除结点的左子树,前驱结点的父节点右子树指向空,避免变成圆形链。
if(frontNode.left == null) {
frontNode.left = current.left;
frontParentNode.right = null;
}
//第二种情况,如果前驱结点不为叶子结点,根据二叉搜素树的特性,它必然有左孩子。那么前驱结点的左孩子的左孩子指向删除结点的左子树,避免变成圆形链。
else {
frontNode.left.left = current.left;
frontParentNode.right = null;
}
}
// 3.3 判断删除结点是否为根结点,如果是,直接把删除结点的父结点指向删除结点,同时根节点要重新指向前驱结点。
if(current == this.root) {
parentNode = current;
this.root = frontNode;
}
// 3.4删除结点
isLeftChild ? parentNode.left = frontNode : parentNode.right = frontNode;
}
}
}
var bst = new BinarySearchTree();
bst.insert(11);
bst.preOrderTraverse();
bst.midOrderTraverse();
bst.postOrderTraverse();
bst.reMove(11)
bst.preOrderTraverse();