二叉搜索树
二叉搜索树也叫二叉排序树:因为它具有排序的效果
- 左子树的节点都比当前节点小
- 右子树的节点都比当前节点大
var arr = [];
for (var i = 0; i < 20; i++) {
arr.push(Math.floor(Math.random() * 100));
}
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
function addNode(root, num) {
if (root == null) return;
if (root.value == num) return;
if (root.value < num) {
// 比当前节点大
if (root.right == null) root.right = new Node(num); //右侧为空创建节点
else addNode(root.right, num); //如果右侧不为空就对右侧递归
} else {
//比当前节点小
if (root.left == null) root.left = new Node(num); //左侧为空创建节点
else addNode(root.left, num); //如果左侧不为空就对左侧递归
}
}
// 创建二叉搜索树
function BuildSearchTree(arr) {
if (arr == null && arr.length == 0) return null;
var root = new Node(arr[0]);
for (var i = 0; i < arr.length; i++) {
// 把每个节点加进去
addNode(root, arr[i]);
}
return root;
}
//使用二叉搜索树搜索
function searchTree(root, target) {
if (root == null) return false;
if (root.value == target) return true;
if (root.value > target) return searchTree(root.left, target);
else return searchTree(root.right, target);
}
var root = BuildSearchTree(arr);
var res = searchTree(root, 98);
console.log(res, arr);
同时,一棵二叉搜索树它的树的深度越深,要搜索的次数就越多,理想情况下,我们让这个二叉树越满,那么它的深度越浅,效率就越好,所以还可以在此基础上继续优化
平衡二叉树
将二叉搜索树变成一颗二叉平衡搜索树
- 根节点的左右子树之间的高度差不超过 1
- 每颗子树都符合它的左右子树的高度差不超过 1
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
var a = new Node("a");
var b = new Node("b");
var c = new Node("c");
var d = new Node("d");
var e = new Node("e");
var f = new Node("f");
var g = new Node("g");
var h = new Node("h");
var j = new Node("j");
a.left = b;
a.right = c;
b.left = d;
b.right = e;
c.left = f;
c.right = g;
d.right = h;
e.right = j;
// 获取树的深度
function getDeep(root) {
if (root == null) return 0;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
return Math.max(leftDeep, rightDeep) + 1;
}
//是否是平衡二叉树
function isBalance(root) {
if (root == null) return true;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) > 1) {
//不平衡
return false;
} else {
return isBalance(root.left) && isBalance(root.right);
}
}
// console.log(isBalance(a));
// console.log(getDeep(e));
二叉树的单旋操作(左单旋、右单旋)
- 某一结点不平衡,左边浅、右边深,左单旋
- 旋转节点:不平衡的节点
- 新根:旋转之后成为根几点的节点
- 变化分支:父级节点发生变化的分支
- 不变分支:父级节点不变的分支
左单旋时:
旋转节点:不平衡的节点
新根:右子树的根节点
变化分支:旋转节点的右子树的左子树
不变分支:旋转节点的右子树的右子树
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
var node2 = new Node("2");
var node5 = new Node("5");
var node3 = new Node("3");
var node6 = new Node("6");
node2.right = node5;
node5.left = node3;
node5.right = node6;
// 获取树的深度
function getDeep(root) {
if (root == null) return 0;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
return Math.max(leftDeep, rightDeep) + 1;
}
function isBalance(root) {
if (root == null) return true;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) > 1) {
//不平衡
return false;
} else {
return isBalance(root.left) && isBalance(root.right);
}
}
// 左旋
function leftRotate(root) {
// 找到新的根
var newRoot = root.right;
// 找到变化分支
var changeTree = root.right.left;
// 当前节点的右孩子改变为变化分支
root.right = changeTree;
// 新根的左孩子为旋转节点
newRoot.left = root;
return newRoot;
}
// 右旋
function rightRotate(root) {
// 新根
var newRoot = root.left;
// 变化分支
var changeTree = root.left.right;
// 当前节点的左孩子改变为变化分支
root.left = changeTree;
// 新根的右孩子为旋转节点
newRoot.right = root;
return newRoot;
}
// 传入不平衡二叉树变为平衡二叉树
function change(root) {
//返回平衡之后的根节点
if (isBalance(root)) return root;
if (root.left != null) root.left = change(root.left);
if (root.right != null) root.right = change(root.right);
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) < 2) {
return root; //平衡
} else if (leftDeep > rightDeep) {
// 不平衡,左边深,右旋
return rightRotate(root);
} else {
// 不平衡,右编深,左旋
return leftRotate(root);
}
return root;
}
console.log(isBalance(node2));
var newRoot = change(node2);
console.log(isBalance(newRoot));
二叉树的双旋
左右双旋、右左双旋(就是在单旋的基础上,多做一步判断,变化分支在最大深度上,就需要对他再单旋)
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
var node8 = new Node("8");
var node7 = new Node("7");
var node6 = new Node("6");
var node5 = new Node("5");
var node2 = new Node("2");
node8.left = node7;
node7.left = node6;
node6.left = node5;
node5.left = node2;
// 获取树的深度
function getDeep(root) {
if (root == null) return 0;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
return Math.max(leftDeep, rightDeep) + 1;
}
function isBalance(root) {
if (root == null) return true;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) > 1) {
//不平衡
return false;
} else {
return isBalance(root.left) && isBalance(root.right);
}
}
// 左旋
function leftRotate(root) {
// 找到新的根
var newRoot = root.right;
// 找到变化分支
var changeTree = root.right.left;
// 当前节点的右孩子改变为变化分支
root.right = changeTree;
// 新根的左孩子为旋转节点
newRoot.left = root;
return newRoot;
}
// 右旋
function rightRotate(root) {
// 新根
var newRoot = root.left;
// 变化分支
var changeTree = root.left.right;
// 当前节点的左孩子改变为变化分支
root.left = changeTree;
// 新根的右孩子为旋转节点
newRoot.right = root;
return newRoot;
}
// 传入不平衡二叉树变为平衡二叉树
function change(root) {
//返回平衡之后的根节点
if (isBalance(root)) return root;
if (root.left != null) root.left = change(root.left);
if (root.right != null) root.right = change(root.right);
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) < 2) {
return root; //平衡
} else if (leftDeep > rightDeep) {
// 不平衡,左边深,右旋
var changeTree = getDeep(root.left.right); //变化分支
var noChangeTree = getDeep(root.left.left); //非变化
if (changeTree > noChangeTree) {
// 如果最大深度在变化分支上
root.left = leftRotate(root.left);
}
return rightRotate(root);
} else {
// 不平衡,右编深,左旋
var changeTree = getDeep(root.right.left); //变化分支
var noChangeTree = getDeep(root.right.right); //非变化
if (changeTree > noChangeTree) {
// 如果最大深度在变化分支上
root.right = rightRotate(root.right);
}
return leftRotate(root);
}
}
let newRoot = change(node8);
console.log(newRoot);
二叉树的双旋
左左双旋、右右双旋特殊情况
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
var node8 = new Node("8");
var node7 = new Node("7");
var node6 = new Node("6");
var node5 = new Node("5");
var node2 = new Node("2");
node8.left = node7;
node7.left = node6;
node6.left = node5;
node5.left = node2;
// 获取树的深度
function getDeep(root) {
if (root == null) return 0;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
return Math.max(leftDeep, rightDeep) + 1;
}
function isBalance(root) {
if (root == null) return true;
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) > 1) {
//不平衡
return false;
} else {
return isBalance(root.left) && isBalance(root.right);
}
}
// 左旋
function leftRotate(root) {
// 找到新的根
var newRoot = root.right;
// 找到变化分支
var changeTree = root.right.left;
// 当前节点的右孩子改变为变化分支
root.right = changeTree;
// 新根的左孩子为旋转节点
newRoot.left = root;
return newRoot;
}
// 右旋
function rightRotate(root) {
// 新根
var newRoot = root.left;
// 变化分支
var changeTree = root.left.right;
// 当前节点的左孩子改变为变化分支
root.left = changeTree;
// 新根的右孩子为旋转节点
newRoot.right = root;
return newRoot;
}
// 传入不平衡二叉树变为平衡二叉树
function change(root) {
//返回平衡之后的根节点
if (isBalance(root)) return root;
if (root.left != null) root.left = change(root.left);
if (root.right != null) root.right = change(root.right);
var leftDeep = getDeep(root.left);
var rightDeep = getDeep(root.right);
if (Math.abs(leftDeep - rightDeep) < 2) {
return root; //平衡
} else if (leftDeep > rightDeep) {
// 不平衡,左边深,右旋
var changeTree = getDeep(root.left.right); //变化分支
var noChangeTree = getDeep(root.left.left); //非变化
if (changeTree > noChangeTree) {
// 如果最大深度在变化分支上
root.left = leftRotate(root.left);
}
var newRoot = rightRotate(root);
newRoot.right = change(newRoot.right); //右侧分支再平衡
newRoot = change(newRoot);
return newRoot;
} else {
// 不平衡,右编深,左旋
var changeTree = getDeep(root.right.left); //变化分支
var noChangeTree = getDeep(root.right.right); //非变化
if (changeTree > noChangeTree) {
// 如果最大深度在变化分支上
root.right = rightRotate(root.right);
}
var newRoot = leftRotate(root);
newRoot.left = change(newRoot.left);
newRoot = change(newRoot);
return newRoot;
}
}
console.log(isBalance(node8));
let newRoot = change(node8);
console.log(newRoot);
console.log(isBalance(node8));
234 树的由来
树的分叉越多,层数越少,但是树的结构就会越复杂(经计算,4 个分叉时,树的性能最好)
234 树:我们希望有一棵树最多有 4 个分叉
- 234 树的子节点永远在最后一层
- 234 树永远平衡(每一个路径高度都相同)
但是分支变多,那么树的复杂度就会变大,所以希望对 234 树简化
红黑树
基础是234 树 性质:
- 节点是红色或者黑色(红色代表虚拟节点不是一个单独的节点)
- 根节点是黑色
- 每个红色节点的两个子节点都是黑色,(从每个叶子到根的所有路径上不得有两个连续的红色节点)
- 从任一节点到其每个叶子的所有路径包含相同数目的黑色节点(红色节点是虚拟的,不计数)
红黑树就是二次平衡排序树,只是每个节点赋予了它不同的意义