# javascript数据结构之树（二叉搜索树，平衡二叉树，红黑树）

Charon

## 一种非顺序数据结构--树。

#### 二叉树和二叉搜索树

1. insert(key) 树里插入键
2. search(key) 树里查找一个键
3. inOrderTraverse() 中序遍历所有(上行顺序遍历，先左，自身，右)
4. preOrderTraverse() 先序遍历所有(先自身，左，右)
5. postOrderTraverse() 后序遍历所有(先左，右，自身)
6. min() 获取树最小值
7. max() 获取树最大值
8. remove(key) 删除树里某个键

``````const Compare = {
LESS_THAN:-1,
BIGGER_THAN:1
}
function defaultCompare(a,b){
if(a===b){
return 0;
}
return a<b?Compare.LESS_THAN:Compare.BIGGER_THAN;
}``````
``````class Node{
constructor(key){
this.key = key;
this.left = null;
this.right = null;
}
}

class BinarySearchTree{
constructor(compareFn = defaultCompare){
this.compareFn = compareFn;
this.root = null;
}
insert(key){
if(!this.root){
this.root = new Node(key);
}else{
this.insertNode(this.root,key);
}
}
insertNode(node,key){//查找对比键大小插入,最后生成结构如上图
if(this.compareFn(key,node.key)===Compare.LESS_THAN){
if(!node.left){
node.left = new Node(key);
}else{
this.insertNode(node.left,key);
}
} else {
if(!node.right){
node.right = new Node(key);
}else{
this.insertNode(node.right,key);
}
}
}
search(key){
return this.searachNode(this.root,key);
}
searachNode(node,key){
if(!node){
return false;
}
if(this.compareFn(key,node.key)===Compare.LESS_THAN){
return this.searachNode(node.left,key);
}else if(this.compareFn(key,node.key)===Compare.BIGGER_THAN){
return this.searachNode(node.right,key);
}else{
return true;//可以根据自己需要返回
}
}
min(){
return this.minNode(this.root);
}
minNode(node){
let current = node;
while(current&&current.left){
current = current.left;
}
return current;
}
max(){
return this.maxNode(this.root);
}
maxNode(node){
let current = node;
while(current&&current.right){
current = current.right;
}
return current;
}
remove(key){
this.root = this.removeNode(this.root,key)
}
removeNode(node,key){
if(!node){
return null;
}
if(this.compareFn(key,node.key)===Compare.LESS_THAN){
//向左查找
node.left = this.removeNode(node.left,key);
return node;
}else if(this.compareFn(key,node.key)===Compare.BIGGER_THAN){
//向右查找
node.right = this.removeNode(node.right,key);
return node;
}else{//找到了
if(!node.left&&!node.right){
node = null;
return node;
}
if(!node.left){
node = node.right;
return node;
}else if(!node.right){
node = node.left;
return node;
}
//第三种情况，移除的节点有两个子节点，先找到要移除节点右边子树里最小的节点，然后移到当前删除位置，然后这会会有两个相同的键（当前删除位置===右侧子树最小节点），所以把右侧子树最小节点删掉。
const aux = this.minNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right,aux.key);
return node;
}
}
inOrderTraverse(cb){
this.inOrderTraverseNode(this.root,cb);
}
inOrderTraverseNode(node,cb){
if(node){
this.inOrderTraverseNode(node.left,cb);
cb(node.key);
this.inOrderTraverseNode(node.right,cb);
}
}
preOrderTraverse(cb){
this.preOrderTraverseNode(this.root,cb)
}
preOrderTraverseNode(node,cb){
if(node){
cb(node.key);
this.preOrderTraverseNode(node.left,cb);
this.preOrderTraverseNode(node.right,cb);
}
}
postOrderTraverse(cb){
this.postOrderTraverseNode(this.root,cb);
}
postOrderTraverseNode(node,cb){
if(node){
this.postOrderTraverseNode(node.left,cb);
this.postOrderTraverseNode(node.right,cb);
cb(node.key);
}
}
}

const printNode = (value)=>console.log(value);//可以用此做回掉测试``````

#### 自平衡二叉树(AVL树)

AVL是一颗自平衡树，在添加或者移除节点的时候会尝试保证自平衡，任何一个节点（不论深度），左侧字树和右侧字数最多相差1。

AVL树需要根据节点高度来计算平衡因子，根据平衡因子来判断，是否需要旋转平衡。

LL：向右的单旋转

RR:向左的单旋转

LR:先左后右,先RR转再LL转,这种情况出现于不平衡节点的左侧节点高度大于右侧节点高度，并且左侧节点右侧较重，这种情况可以先对左侧节点进行左旋转修复，然后在对不平衡节点进行右旋转修复。

RL:先右后左,先LL转再RR转，这种情况出现于不平衡节点的右侧节点高度大于左侧节点高度，并且右侧节点左侧较重，这种情况可以先对右侧节点进行右旋转修复，然后对不平衡节点进行一个左旋转修修复。

``````const BalanceFactor = {//平衡因子的常量
UNBALANCED_RIGHT:1,
SLIGHTLY_UNBALANCED_RIGHT:2,
BALANCED:3,
SLTGHTLY_UNBALANCED_LEFT:4,
UNBALANCED_LEFT:5
}
class AVLTree extends BinarySearchTree{
constructor(compareFn=defaultCompare){
super(compareFn);
this.compareFn=compareFn;
this.root = null;
}
getNodeHeight(node){
if(!node){
return -1
}
return Math.max(this.getNodeHeight(node.left),
this.getNodeHeight(node.right))+1
}
getBalanceFactor(node){
const heightDifference = this.getNodeHeight(node.left)-this.getNodeHeight(node.right);
switch(heightDifference){
case -2:
return BalanceFactor.UNBALANCED_RIGHT;
case -1:
return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT;
case 1:
return BalanceFactor.SLTGHTLY_UNBALANCED_LEFT;
case 2:
return BalanceFactor.UNBALANCED_LEFT;
default:
return BalanceFactor.BALANCED;
}
}
rotationLL(node){//右单旋转
const tmp = node.left;
node.left = tmp.right;
tmp.right = node;
return node;
}
rotationRR(node){//左单旋转
const tmp = node.right;
node.right = tmp.left;
tmp.left = node;
return tmp;
}
rotationLR(node){
node.left = this.rotationRR(node.left);
return this.rotationLL(node);
}
rotationRL(node){
node.right = this.rotationLL(node.right);
return this.rotationRR(node);
}
insert(key){
this.root = this.insertNode(this.root,key)
}
insertNode(node,key){
if(!node){
return new Node(key);
}else if(this.compareFn(key,node.key)===Compare.LESS_THAN){
//向左继续
node.left = this.insertNode(node.left,key);
}elso if(this.compareFn(key,node.key)===Compare.BIGGER_THAN){
//向右继续
node.right = this.insertNode(node.right,key);
}else{//不比当前key大也不比当前key小，等于当前重复
return node;//重复的键
}
//递归插入完成后，根据函数依次弹栈，来计算当前的节点的平衡因子，查看是否失衡
const balanceFactor = this.getBalanceFactor(node);
if(balanceFactor === BalanceFactor.UNBALANCED_LEFT){//等于2，左侧子节点失衡
if(this.compareFn(key,node.left.key)===Compare.LESS_THAN){//查看插入的节点是插到了当前节点左子节点的下左侧,符合LL，详细可以配上面图看
node = this.rotationLL(node);
}else {//右，符合LR
return this.rotationLR(node);
}
}
if(balanceFactor === BalanceFactor.UNBALANCED_RIGHT){//-2右侧失衡
if(this.compareFn(key,node.right.key)===Compare.BIGGER_THAN){//查看插入的节点是插到了当前节点右子节点的下右侧,符合RR，详细可以配上面图看
node = this.rotationRR(node);
}else{//下左，符合RL
return this.rotationRL(node);
}
}
return node;
}
removeNode(node){
node = super.removeNode(node,key);
if(!node){
return node;
}
const balanceFactor = this.getBalanceFactor(node);
if(balanceFactor === BalanceFactor.UNBALANCED_LEFT){//左子节点失衡
const balanceFactorLeft = this.getBalanceFactor(node.left);
//判断左边是那种情况，是进行LL转还是LR转
if(balanceFactorLeft===BalanceFactor.SLTGHTLY_UNBALANCED_LEFT||balanceFactorLeft.BALANCED){//差值为1或者其他情况的时候进行符合LL
return this.rotationLL(node);
}
if(balanceFactorLeft===BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT){//左侧差值为-1，符合LR
return this.rotationLR(node);
}
}
if(balanceFactor === BalanceFactor.UNBALANCED_RIGHT){//差值为-2，右侧子节点不平衡
const balanceFactorRight = this.getBalanceFactor(node.right);
if(balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT||balanceFactorRight===BalanceFactor.BALANCED){//和左相反，差值-1或其他符合RR
return return this.rotationRR(node);
}
if(balanceFactorRight === BalanceFactor.SLTGHTLY_UNBALANCED_LEFT){//差值为1符合RL
return this.rotationRL(node);
}
}
return node;
}
}``````

#### 红黑树

##### 所以红黑树就是这种设计思路(近似平衡)了．

1. 每个节点不是红的就是黑的。
2. 树的根节点都是黑的。
3. 所有叶节点都是黑的([注意：这里叶子节点，是指为空(NIL或NULL)的叶子节点！])
4. 如果一个节点是红的，那么他的两个子节点都是黑的。
5. 不能有两个相邻的红节点，一个红节点不能有红的父节点或子节点也就是说，红色节点是被黑色节点隔开的
6. 从给定的节点到他的后代节点（null节点）的所有路径包含相同数量的黑色节点。

1. 任何相邻的节点都不能同时为红色，也就是说，红色节点是被黑色节点隔开的；
2. 每个节点，从该节点到达其可达叶子节点的所有路径，都包含相同数目的黑色节点；

### 插入

###### `红黑树规定，插入的节点必须是红色的。而且，二叉查找树中新插入的节点都是放在叶子节点上`。所以，关于插入操作的平衡调整，有这样两种特殊情况，但是也都非常好处理。
1. 如果插入节点的父节点是黑色的，那我们什么都不用做，它仍然满足红黑树的定义。
2. 如果插入的节点是根节点，那我们直接改变它的颜色，把它变成黑色就可以了。
3. 其他：都会违背红黑树的定义，于是我们就需要进行调整，调整的过程包含两种基础的操作：左右旋转和改变颜色。
``````const Color = {
RED:'red',
BLACK:'black'
}
class RedBlackNode extends Node{
constructor(key){
this.key = key;
this.color = Color.RED;
this.parent = null;
}
isrRed(){
return this.color === Color.RED;
}
}
class RedBlackTree extends BinarySearchTree{
constructor(compareFn = defaultCompare){
super(compareFn);
this.compareFn = compareFn;
this.root = null;
}
insert(key){
if(!this.root){
this.root = new RedBlackNode(key);
this.root.color = Colors.BLACK;
}else{
const newNode = this.insertNode(this.root,key);
this.fixTreeProperties(newNode);
}
}
insertNode(node,key){
if(this.compareFn(key,node.key)===Compare.LESS_THAN){
if(!node.left){
node.left = new RedBlackNode(key);
node.left.parent = node;
return node.left;
}else{
return this.insertNode(node.left,key);
}
}else if(this.compareFn(key,node.key)===Compare.BIGGER_THAN){
if(!node.right){
node.right = new RedBlackNode(key);
node.right.parent = node;
return node.right;
}else{
return this.insertNode(node.right,key);
}
}else{//相同
node.key = key; //key万一是复杂对象。
return node;
}

}
fixTreeProperties(node){
while(node&&node.parent&&node.parent.isRed()&&node.color===!==Color.BLACK){
let parent = node.parent;
const grandParent = parent.parent;
if(grandParent&&grandParent.left===parent){
//父节点是左侧子节点
const uncle = grandParent.right;
if(uncle&&uncle.color === Color.RED){
//左侧叔父节点是红色，重新填色，隔开颜色
grandParent.color = Color.RED;
uncle.color = Color.BALCK;
parent.color= Color.BALCK;
node = grandParent;
}else{
//节点是右侧子节点-左旋转
if(node === parent.right){
this.rotationRR(parent);
node = parent;
parent = node.parent;
}
//节点是左侧子节点-右旋转
this.rotationLL(grandParent);
parent.color = Color.BLACK;
grandParent.color = Color.RED;
node = parent;
}

}else{//父节点是右侧节点
const uncle = grandParent.left;
//左侧叔父节点是红色，重新填色，隔开颜色
if(uncle&&uncle.color === Color.RED){
grandParent.color = Color.RED;
uncle.color = Color.BALCK;
parent.color= Color.BALCK;
node = grandParent;
}else{
if(node === parent.left){
//节点是左侧子节点-右旋转
this.rotationLLarent);
node = parent;
parent = node.parent;
}
//节点是右侧子节点旋转
this.rotationRRrandParent);
parent.color = Color.BLACK;
grandParent.color = Color.RED;
node = parent;

}
}
}
this.root.color = Color.Black;
}
rotationLL(node){//多了一步设置对应父级
const tmp = node.left;
node.left = tmp.right;
if(tmp.right&&tmp.right.key){
tmp.right.parent = node;
}
tmp.parent = node.parent;
if(!node.parent){//顶级了
this.root =tmp;
}else{
if(node === node.parent.left){
node.parent.left = tmp;
}else{
node.parent.right = tmp;
}
}
tmp.right = node;
node.parent = tmp;

}
rotationRR(node){//左旋转
const tmp = node.right;
node.right = tmp.left;
if(tmp.left&&tmp.left.key){
tmp.left.parent = node;
}
tmp.parent = node.parent;
if(!node.parent){
this.root = node;
}else{
if(node === node.parent.left){
node.parent.left=tmp;
}else{
node.parent.right = tmp;
}
}
tmp.left = node;
node.parent = tmp;
}
}``````

22 声望
11 粉丝