一、什么是二叉搜索树
二叉查找树(英语:Binary Search Tree),也称为二叉查找树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
二、二叉搜索树的有缺点
优点:二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)。
缺点:对于分布不均匀的不平衡树(类似链表),查找效率从O(log n) 变为了O(N),但是可以经由树高改良后的平衡树将搜索、插入、删除的时间复杂度都维持在、 O(log n),如AVL树、红黑树等。
三、属性与方法
为了方便,数据就直接用key表示
增:
- insert(key),插入一个值
删:
- remove(key),从树中移除指定值
查:
- search(key),存在指定值返回true,不存在返回false
其他:
- inOrderTraverse,中序遍历所有节点,以字符串形式返回
- preOrderTraverse,先序遍历所有节点,以字符串形式返回
-
postOrderTraverse,后序遍历所有节点,以字符串形式返回
-
min,返回树中最小值
- max,返回树中最大值
这里的先序、中序、后序可以理解为,什么时候处理节点(将节点的值加入输出对象里),先处理就算先序,中间处理就是中序,最后处理就是后序,如下面就是中序的的部分代码:
if (node != null) {
// 处理经过的节点的左子节点(想想回溯和函数调用栈)
resultString = this.inOraderTraverseNode(node.left, resultString);
// 处理经过节点
resultString += node.key + " ";
// 处理经过的右子节点(就算节点为空也不会改变resultString)
resultString = this.inOraderTraverseNode(node.right, resultString);
}
四、具体实现
function BinarySearchTree() {
function Node(key) {
this.key = key;
this.left = null;
this.right = null;
}
//属性
this.root = null;
// 方法
// 1.增
BinarySearchTree.prototype.insert = function (key) {
let newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
};
BinarySearchTree.prototype.insertNode = function (node, newNode) {
if (newNode.key < node.key) {
//向左查找
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else if (node.key == newNode.key) {
return false;
} else {
//向右查找
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
};
// 删
BinarySearchTree.prototype.remove = function (key) {
//1.寻找要删除的节点
let current = this.root;
let parent = null;
let isLeftChild = true;
while (current.key != key) {
parent = current;
if (current.key > key) {
current = current.left;
isLeftChild = true;
} else {
current = current.right;
isLeftChild = false;
}
// 都找完没有发现key的情况
if (current == null) return false;
}
//2.删除节点
//2.1 删除叶子节点
if (!current.left && !current.right) {
if (current === this.root) {
this.root = null;
} else {
if (isLeftChild) {
parent.left = null;
} else {
parent.right = null;
}
}
}
//2.2 有一个子节点的节点
else if (current.right == null) {
if (current === this.root) {
this.root = current.left;
current.left = null;
} else {
// parent?
if (isLeftChild) {
parent.left = current.left;
} else {
parent.right = current.left;
}
}
} else if (current.left == null) {
if (current == this.root) {
this.root = current.right;
current.right = null;
} else {
// parent?
if (isLeftChild) {
parent.left = current.right;
} else {
parent.right = current.right;
}
}
}
//2.3 有两个节点的节点
else {
// 1.获取后续节点
let succeed = this.getSuccssor(current);
// 2.root?
if (current == this.root) {
this.root = succeed;
} else if (isLeftChild) {
parent.left = succeed;
} else {
parent.right = succeed;
}
// 3.删除节点的左子树
succeed.left = current.left;
}
};
BinarySearchTree.prototype.getSuccssor = function (delNode) {
// 定义变量,保存继承节点
let successor = delNode;
let current = delNode.right;
let successorParent = delNode;
// 循环查找右子树最小值
while (current != null) {
successorParent = successor;
successor = current;
current = current.left;
}
// 判断继承节点是否是delNode的右节点
if (successor != delNode.right) {
// 这儿是嫁接切除操作
successorParent.left = successor.right;
successor.right = delNode.right;
}
return successor;
};
// 查
BinarySearchTree.prototype.search = function (key) {
let node = this.root;
while (node != null) {
if (key < node.key) {
node = node.left;
} else if (key > node.key) {
node = node.right;
} else {
return true;
}
}
return false;
};
// 中序遍历
BinarySearchTree.prototype.inOraderTraverse = function () {
return this.inOraderTraverseNode(this.root);
};
BinarySearchTree.prototype.inOraderTraverseNode = function (
node,
resultString = "中序:"
) {
if (node != null) {
// 处理经过的节点的左子节点(想想回溯和函数调用栈)
resultString = this.inOraderTraverseNode(node.left, resultString);
// 处理经过节点
resultString += node.key + " ";
// 处理经过的右子节点(就算节点为空也不会改变resultString)
resultString = this.inOraderTraverseNode(node.right, resultString);
}
return resultString;
};
// 先序遍历
BinarySearchTree.prototype.preOraderTraverse = function () {
return this.preOraderTraverseNode(this.root);
};
BinarySearchTree.prototype.preOraderTraverseNode = function (
node,
resultString = "先序:"
) {
if (node != null) {
// 处理经过节点
resultString += node.key + " ";
// 处理经过的节点的左子节点(想想回溯和函数调用栈)
resultString = this.preOraderTraverseNode(node.left, resultString);
// 处理经过的右子节点(就算节点为空也不会改变resultString)
resultString = this.preOraderTraverseNode(node.right, resultString);
}
return resultString;
};
// 后序遍历
BinarySearchTree.prototype.postOraderTraverse = function () {
return this.postOraderTraverseNode(this.root);
};
BinarySearchTree.prototype.postOraderTraverseNode = function (
node,
resultString = "后序:"
) {
if (node != null) {
// 处理经过的节点的左子节点(想想回溯和函数调用栈)
resultString = this.postOraderTraverseNode(node.left, resultString);
// 处理经过的右子节点(就算节点为空也不会改变resultString)
resultString = this.postOraderTraverseNode(node.right, resultString);
// 处理经过节点
resultString += node.key + " ";
}
return resultString;
};
// min
BinarySearchTree.prototype.min = function () {
if (this.root === null) return null;
let node = this.root;
while (node.left != null) {
node = node.left;
}
return node.key;
};
// max
BinarySearchTree.prototype.max = function () {
if (this.root === null) return null;
let node = this.root;
while (node.right != null) {
node = node.right;
}
return node.key;
};
}
// test
let bst = new BinarySearchTree();
bst.insert(9);
bst.insert(11);
bst.insert(10);
bst.insert(8);
bst.insert(8);
bst.insert(6);
bst.insert(5);
bst.insert(12);
bst.insert(13);
bst.insert(20);
bst.insert(7);
console.log(bst.preOraderTraverse());
console.log(bst.inOraderTraverse());
console.log(bst.postOraderTraverse());
console.log(bst.max());
console.log(bst.search(21));
console.log(bst.search(20));
bst.remove(9);
console.log(bst.inOraderTraverse());