参考
- b站视频链接:www.bilibili.com/video/BV1x7…
- 参考的github笔记:github.com/XPoet/JS-Da…
树(Tree)
- 非线性结构
- 查找、修改、删除效率较数组、链表快,慢于哈希表,但空间利用率高
二叉搜索树(Binary Search Tree)
-
非空左子树的所有键值小于其根节点键值
-
非空右子树的所有键值大于其根节点键值
-
左、右子树本身也都是二叉搜索树
- 类似有序数组的二分查找
-
BST的remove(key)
- current:要删除的节点
- parent:要删除的节点的父节点
- isLeftChild:current是parent的左子树为true,右子树为false
- 1、删除的节点无后继节点
- current是根节点,直接删除根节点
- 如:remove(53)
- 如:remove(53)
- current是叶子节点,通过isLeftChild,将parent.left 或 parent.right置为null
- 如:remove(23)
- 如:remove(23)
- current是根节点,直接删除根节点
- 2、删除的节点仅有一个后继节点
- 后继节点是左子节点
- current是根节点,将根指针指向左子节点
- remove(53)
- remove(53)
- current是非根节点,通过isLeftChild,将parent.left 或 parent.right 指向 current.left
- 如:remove(45)
- 如:remove(45)
- current是根节点,将根指针指向左子节点
- 后继节点是右子节点
- current是根节点,将根指针指向右子节点
- 如:remove(53)
- 如:remove(53)
- current是非根节点,用isLeftChild,将parent.left 或 parent.right 指向 current.right
- 如:remove(3)
- 如:remove(3)
- current是根节点,将根指针指向右子节点
- 后继节点是左子节点
- 3、删除的节点有两个后继节点
- delNode:要删除的节点
- successor:替换删除节点位置的节点(此处使用delNode右子树键值最小的节点)
- successorParent:successor的父接电脑
- 1、先找到要替换delNode的节点successor
- 1-1、从delNode的右子树开始遍历它的左子树,找到最小的节点
- 1-2、如果delNode的右子树最小节点是delNode.right
- 1-2、不是,则需要将替换节点的右子树赋值给successorParent.left,并将delNode的右子树赋值给successor.right
- 1-3、返回找到的最小节点
- 2、current是根节点,将根指针指向successor
- 2、current是非根节点,用isLeftChild,将parent.left 或 parent.right 指向 successor
- 3、将要删除的节点的左子树赋值给successor.left
function BST() {
// 根节点
this.root = null;
// Node(key):创建节点存储key
function Node(key){
this.key = key;
this.left = null;
this.right = null;
}
// insert(key):插入新的节点,3-1是非递归写法,3-2是递归写法
BST.prototype.insert = function(key){
// 1、创建新节点
var newNode = new Node(key);
// 2、根节点是否为null
if(!this.root)
this.root = newNode;
// // 3-1、非递归写法:遍历判断节点大小
// var currentNode = this.root;
// while(currentNode){
// if(currentNode.key == newNode.key){
// return false;
// }
// else if(currentNode.key > newNode.key){
// // 左子树
// if(!currentNode.left)
// currentNode.left = newNode;
// currentNode = currentNode.left;
// }else{
// // 右子树
// if(!currentNode.right)
// currentNode.right = newNode;
// currentNode = currentNode.right;
// }
// }
// 3-2、递归写法
else
return this.insertNode(this.root,newNode);
// return true;
}
// 3-2、递归写法:均可转成while循环的非递归完成
BST.prototype.insertNode = function(Node,newNode){
if(Node.key == newNode.key){
return false;
}
else if(Node.key > newNode.key){
// 左子树
if(!Node.left){
Node.left = newNode;
return true;
}
return this.insertNode(Node.left,newNode);
}else{
// 右子树
if(!Node.right){
Node.right = newNode;
return true;
}
return this.insertNode(Node.right,newNode);
}
}
// search(key):查找键为key的节点,成功->currentNode,失败->false
BST.prototype.search = function(key){
// 从根节点开始比较key大小
var currentNode = this.root;
while(currentNode){
if(currentNode.key == key){
return currentNode;
}else if(currentNode.key > key){
// 左子树
currentNode = currentNode.left;
}else{
// 右子树
currentNode = currentNode.right;
}
}
return false;
}
// inOrderTraverse():中序遍历,左根右
BST.prototype.inOrderTraverse = function(){
if(this.root){
var OrderStr = [];
this.inOrderTraverseNode(this.root,OrderStr);
return OrderStr.join("-");
}
return false;
}
BST.prototype.inOrderTraverseNode = function(Node,OrderStr){
if(Node){
// 遍历左子树
this.inOrderTraverseNode(Node.left,OrderStr);
// 处理经过的节点
OrderStr.push(Node.key);
// 遍历右子树
this.inOrderTraverseNode(Node.right,OrderStr);
}
}
// preOrderTraverse():先序遍历,根左右
BST.prototype.preOrderTraverse = function(){
if(this.root){
var OrderStr = [];
this.preOrderTraverseNode(this.root,OrderStr);
return OrderStr.join("-");
}
return false;
}
BST.prototype.preOrderTraverseNode = function(Node,OrderStr){
if(Node){
// 处理经过的节点
OrderStr.push(Node.key);
// 遍历左子树
this.preOrderTraverseNode(Node.left,OrderStr);
// 遍历右子树
this.preOrderTraverseNode(Node.right,OrderStr);
}
}
// postOrderTraverse():后序遍历,左右根
BST.prototype.postOrderTraverse = function(){
if(this.root){
var OrderStr = [];
this.postOrderTraverseNode(this.root,OrderStr);
return OrderStr.join("-");
}
return false;
}
BST.prototype.postOrderTraverseNode = function(Node,OrderStr){
if(Node){
// 遍历左子树
this.postOrderTraverseNode(Node.left,OrderStr);
// 遍历右子树
this.postOrderTraverseNode(Node.right,OrderStr);
// 处理经过的节点
OrderStr.push(Node.key);
}
}
// min:返回最小的键
BST.prototype.min = function(){
// 最左的节点
var currentNode = this.root;
while(currentNode.left){
currentNode = currentNode.left;
}
return currentNode;
}
// max:返回最大的键
BST.prototype.max = function(){
// 最右的节点
var currentNode = this.root;
while(currentNode.right){
currentNode = currentNode.right;
}
return currentNode;
}
// remove(key):从树中移除某个key
/*
无此节点:false
有此节点:
当前节点
1、没有子节点(删除叶节点)
2、只有左节点 | 只有右节点,连接上一层的节点
3、两个子节点
*/
BST.prototype.remove = function(key){
// 1、寻找键为key的节点
var current = this.root;
var parent = null;
var isLeftChild = null;// parent的左、右子树
// 比较根节点,判断应该向左 or 右进行搜索
while(current && current.key != key){
parent = current;
if(current.key > key){
// 左子树
current = current.left;
isLeftChild = true;
}else{
// 右子树
current = current.right;
isLeftChild = false;
}
}
// 空树,遍历到空节点还没找到key
if(!current)
return false;
// 2、没有子节点(删除叶节点 或 删除根节点)
if(!current.left && !current.right){
if(current == this.root){
// 删除根节点
this.root = null;
}else{
// 删除叶子节点
if(isLeftChild){
parent.left = null;
}else{
parent.right = null;
}
}
}
// 3、一个子节点(左节点或右节点 -> 连接父节点)
// 当前节点只有左节点
else if(!current.right){
if(current == this.root){
this.root = current.left;
}else if(isLeftChild){
parent.left = current.left;
}else{
parent.right = current.left;
}
}
// 当前节点只有右节点
else if(!current.left){
if(current == this.root){
this.root = current.right;
}else if(isLeftChild){
parent.left = current.right;
}else{
parent.right = current.left;
}
}
// 4、两个子节点
// 将左子树最大的提上去 或 右子树最小来替换要删除的节点
else {
// 1.右子树中键值最小的节点
var successor = this.getSuccessor(current);
// 2.判断是否是根节点
if(current == this.root){
this.root = successor;
}else if(isLeftChild){
parent.left = successor;
}else{
parent.right = successor;
}
// 3.将删除节点的左子树赋值给successor
successor.left = current.left;
}
}
// 找要删除节点的右子树中最小的节点
BST.prototype.getSuccessor = function (delNode) {
// 1.使用变量保存临时的节点
var successorParent = delNode;
var successor = delNode;
var current = delNode.right; // 要从右子树开始找
// 2.寻找节点
while(current != null){
successorParent = successor;
successor = current;
current = current.left;
}
// 3.找到的节点是左子节点为null,右子节点可能为null
if(successor != delNode.right){
// 将替换节点的右子树赋值给successorParent.left
successorParent.left = successor.right;
// 将删除节点的右子树赋值给successor.right
successor.right = delNode.right;
}
return successor;
}
}
var bst = new BST();
bst.insert(53);
bst.insert(17);
bst.insert(78);
bst.insert(9);
bst.insert(3);
bst.insert(45);
bst.insert(70);
bst.insert(94);
bst.insert(23);
bst.insert(60);
bst.insert(75);
bst.insert(88);
console.log("inOrder:",bst.inOrderTraverse());
console.log("preOrder:",bst.preOrderTraverse());
console.log("postOrder:",bst.postOrderTraverse());
bst.remove(78);
console.log("----remove(78)----");
console.log("inOrder:",bst.inOrderTraverse());
console.log("preOrder:",bst.preOrderTraverse());
console.log("postOrder:",bst.postOrderTraverse());
console.log("min:",bst.min());
console.log("max:",bst.max());
console.log("search:");
console.log(bst.search(3),bst.search(53),bst.search(88));