【从蛋壳到满天飞】JS 数据结构解析和算法实现,所有文章大概的内容以下: Arrays(数组)、Stacks(栈)、Queues(队列)、LinkedList(链表)、Recursion(递归思想)、BinarySearchTree(二分搜索树)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(优先队列)、SegmentTree(线段树)、Trie(字典树)、UnionFind(并查集)、AVLTree(AVL 平衡树)、RedBlackTree(红黑平衡树)、HashTable(哈希表)html
源代码有三个:ES6(单个单个的 class 类型的 js 文件) | JS + HTML(一个 js 配合一个 html)| JAVA (一个一个的工程)node
所有源代码已上传 github,点击我吧,光看文章可以掌握两成,动手敲代码、动脑思考、画图才能够掌握八成。git
本文章适合 对数据结构想了解而且感兴趣的人群,文章风格一如既往如此,就以为手机上看起来比较方便,这样显得比较有条理,整理这些笔记加源码,时间跨度也算将近半年时间了,但愿对想学习数据结构的人或者正在学习数据结构的人群有帮助。github
在以前实现的二分搜索树的问题算法
[1, 2, 3, 4, 5, 6]
后,在现有的二分搜索树中添加一些机制,从而可以维持平衡二叉树的性质,编程
A
delson-V
elsky 和 E.M.L
andis,平衡二叉树数组
在本身实现的堆中引入了彻底二叉树的概念,数据结构
本身实现的线段树也是一种平衡二叉树框架
不管是堆仍是线段树都是平衡二叉树的例子dom
在 AVL 树中定义平衡二叉树
一个叶子节点
相应的高度差都不能超过一,一个节点
它的左右子树高度差不能超过一,O(logn)
级别的。// 知足AVL定义的平衡二叉树
// (12)
// / \
// (8) (18)
// /\ /
// (5)(11)(17)
// /
// (4)
复制代码
对上面图中的 AVL 平衡二叉树进行添加节点
// 添加节点2和节点7后的树
// (12)
// / \
// (8) (18)
// / \ /
// (5) (11) (17)
// / \
// (4) (7)
// /
// (2)
//
复制代码
让上面图中的树维持一个平衡
O(logn)
这个级别的,对于以前所实现的二分搜索树来讲,
实现这个 AVL 平衡二叉树相应的每个节点都记录一下这个节点所在的高度,
其实这个记录很是的简单,对于叶子节点来讲它所对应的高度就是 1,
也就是节点 2 对应的高度为 1;节点 4 下面只有一个叶子节点,那么对应的高度就是 2;
节点 7 也是一个叶子节点,因此它的高度值也是 1;
可是对于节点 5 来讲它有左右两棵子树,左边的子树高度为 2,右边的子树高度为 1,
相应的节点 5 这个节点它的高度就是左右两棵子树中最高的那棵树再加上自身的 1,
那么这样一来节点 5 它的高度就是 2+1=3;很是好理解,节点 11 它的高度就是 1,
其它的如此类推,以下图的标注所示。
// 节点名称加高度 小括号中的是节点名称,中括号中的是高度值
// 【5】(12)
// / \
// 【4】(8) (18)【2】
// / \ /
// 【3】(5) 【1】(11) (17)【1】
// / \
// 【2】(4) (7)【1】
// /
// 【1】(2)
//
复制代码
平衡因子
对整棵树每个节点都标注上高度值以后,
相应的计算一个称之为平衡因子的这样的一个数,
这个名词虽然听起来比较复杂,实际上很是的简单,
它就是指对于每个节点而言,它的左右子树的高度差,
计算高度值的差可使用左子树的高度减去右子树的高度差。
对于节点 2 来讲它是要给叶子节点,
它的左右两棵子树至关因而两棵空树,空树的高度值能够记为 0,
相应的叶子节点的平衡因子其实就是0 - 0 = 0
,最后的结果也为 0;
对于节点 4 来讲它的左子树对应的高度值为 1,右子树为空所对应的高度值为 0,
那么它的平衡因子就是1 - 0 = 1
,最后的结果为 1;
节点 7 是个叶子节点,那么它的平衡因子就是 0;
节点 5 两棵子树的高度差是2 - 1 = 1
,最终的结果为 1;
节点 11 是叶子节点,因此他的平衡因子为 0;
节点 8 的两棵子树的高度查实3 - 1 = 2
,最终的结果为 2。
这就意味着节点 8 这个左右子树的高度差已经超过了一,
经过这个平衡因子就能够看出来这棵树已经不是一棵平衡二叉树了,
以下图这样标注出平衡因子以后,
一旦在一棵树中有一个节点它的平衡因子是大于等于 2 或者小于等于-2 的,
换句话说它的绝对值是等于 2,那么整棵树就再也不是一棵平衡二叉树了。
以下图所示,节点 12 和节点 8 的平衡因子的绝对值都是 2,
那么就说明这两个节点破坏了平衡二叉树对应的那个性质,
这个对应的性质就是 左右子树的高度差超过了一,
一旦能够计算出每个节点的平衡因子,
相应的也能够看出来当前的这棵树是不是一棵平衡二叉树,
不只如此在后续的 AVL 实现中也会借助每个节点
它对应的平衡因子来决定是否要进行一些特殊的操做,
从而来维持整棵树是不是一棵平衡二叉树。
// 1. 节点名称 加高度值 加平衡因子值
// 1. 小括号中的是节点名称,
// 2. 中括号中的是高度值,
// 3. 大括号中的是平衡因子
//
// 2. 平衡因子值 = 左右子树的高度差
// 3. 左右子树的高度差 = 左子树的高度值 - 右子树的高度值
// {2}【5】(12)
// / \
// {2}【4】(8) (18)【2】{1}
// / \ /
// {1}【3】(5) {0}【1】(11)(17)【1】{0}
// / \
// {1}【2】(4) (7)【1】{1}
// /
// {0}【1】(2)
//
复制代码
经过对每个节点标注相应的高度
而后经过这个高度很是简单的计算出平衡因子,
最后在经过平衡因子来决定是否进行一些特殊的操做,
从而维持整棵树是一棵平衡二叉树的性质。
AVLTree
// 自定义AVL树节点 AVLTreeNode
class MyAVLTreeNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.height = 1;
}
// @Override toString 2018-11-24-jwl
toString() {
return this.key + '--->' + this.value + '--->' + this.height;
}
}
// 自定义AVL树 AVLTree
class MyAVLTree {
constructor() {
this.root = null;
this.size = 0;
}
// 比较的功能
compare(keyA, keyB) {
if (keyA === null || keyB === null)
throw new Error("key is error. key can't compare.");
if (keyA > keyB) return 1;
else if (keyA < keyB) return -1;
else return 0;
}
// 获取某个节点的高度 -
getHeight(node) {
// 节点为空 返回0
if (!node) return 0;
// 直接返回这个节点的高度
return node.height;
}
// 获取一个节点的平衡因子 -
getBalanceFactor(node) {
// 节点为空 返回0
if (!node) return 0;
// 左右子树的高度值
const leftHeight = this.getHeight(node.left);
const rightHeight = this.getHeight(node.right);
// 左子树的高度 - 右子树高度的值 = 平衡因子
return leftHeight - rightHeight;
}
// 根据key获取节点 -
getNode(node, key) {
// 先解决最基本的问题
if (node === null) return null;
// 开始将复杂的问题 逐渐缩小规模
// 从而求出小问题的解,最后构建出原问题的解
switch (this.compare(node.key, key)) {
case 1: // 向左找
return this.getNode(node.left, key);
break;
case -1: // 向右找
return this.getNode(node.right, key);
break;
case 0: // 找到了
return node;
break;
default:
throw new Error(
'compare result is error. compare result : 0、 一、 -1 .'
);
break;
}
}
// 添加操做 +
add(key, value) {
this.root = this.recursiveAdd(this.root, key, value);
}
// 添加操做 递归算法 -
recursiveAdd(node, key, value) {
// 解决最简单的问题
if (node === null) {
this.size++;
return new MyAVLTreeNode(key, value);
}
// 将复杂的问题规模逐渐变小,
// 从而求出小问题的解,从而构建出原问题的答案
if (this.compare(node.key, key) > 0)
node.left = this.recursiveAdd(node.left, key, value);
else if (this.compare(node.key, key) < 0)
node.right = this.recursiveAdd(node.right, key, value);
else node.value = value;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
node.height =
1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(node);
// 若是平衡因子的绝对值大于1 说明不知足AVL平衡二叉树的性质了
if (Math.abs(balanceFactor) > 1) {
console.log(
node.toString() + ' unbalanced : ' + balanceFactor + '\r\n'
);
document.body.innerHTML +=
node.toString() + ' unbalanced : ' + balanceFactor + '<br/>';
}
return node;
}
// 删除操做 返回被删除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 删除操做 递归算法 +
recursiveRemove(node, key) {
// 解决最基本的问题
if (node === null) return null;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
return node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
return node;
} else {
// 当前节点的key 与 待删除的key的那个节点相同
// 有三种状况
// 1. 当前节点没有左子树,那么只有让当前节点的右子树直接覆盖当前节点,就表示当前节点被删除了
// 2. 当前节点没有右子树,那么只有让当前节点的左子树直接覆盖当前节点,就表示当前节点被删除了
// 3. 当前节点左右子树都有, 那么又分两种状况,使用前驱删除法或者后继删除法
// 1. 前驱删除法:使用当前节点的左子树上最大的那个节点覆盖当前节点
// 2. 后继删除法:使用当前节点的右子树上最小的那个节点覆盖当前节点
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
} else if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left);
this.size++;
// 开始嫁接 当前节点的左右子树
predecessor.left = node.left;
predecessor.right = node.right;
// 将当前节点从根节点剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接后的新节点
return predecessor;
}
}
}
// 删除操做的两个辅助函数
// 获取最大值、删除最大值
// 之前驱的方式 来辅助删除操做的函数
// 获取最大值
maximum(node) {
// 不再能往右了,说明当前节点已是最大的了
if (node.right === null) return node;
// 将复杂的问题渐渐减少规模,从而求出小问题的解,最后用小问题的解构建出原问题的答案
return this.maximum(node.right);
}
// 删除最大值
removeMax(node) {
// 解决最基本的问题
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 开始化归
node.right = this.removeMax(node.right);
return node;
}
// 查询操做 返回查询到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含该key的元素的判断值 +
contains(key) {
return this.getNode(this.root, key) !== null;
}
// 返回映射中实际的元素个数 +
getSize() {
return this.size;
}
// 返回映射中是否为空的判断值 +
isEmpty() {
return this.size === 0;
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyBinarySearchTreeMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyBinarySearchTreeMap: size = ${ this.size }, data = [ <br/><br/>`;
// 以非递归的前序遍历 输出字符串
let stack = new MyLinkedListStack();
stack.push(this.root);
if (this.root === null) stack.pop();
while (!stack.isEmpty()) {
let node = stack.pop();
if (node.left !== null) stack.push(node.left);
if (node.right !== null) stack.push(node.right);
if (node.left === null && node.right === null) {
mapInfo += ` ${node.toString()} \r\n`;
document.body.innerHTML += ` ${node.toString()} <br/><br/>`;
} else {
mapInfo += ` ${node.toString()}, \r\n`;
document.body.innerHTML += ` ${node.toString()}, <br/><br/>`;
}
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
复制代码
Main
// main 函数
class Main {
constructor() {
this.alterLine('AVLTree Area');
// 千级别
const openCount = 100; // 操做数
const rank = 10000000;
// 生成同一份测试数据的辅助代码
const random = Math.random;
const array = new Array(openCount);
// 生成同一份测试数据
for (var i = 0; i < openCount; i++)
array[i] = Math.floor(random() * rank);
// 建立AVL树实例
const avl = new MyAVLTree();
for (const value of array) avl.add(value);
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
// 页面加载完毕
window.onload = function() {
// 执行主函数
new Main();
};
复制代码
AVLTree
// 自定义AVL树节点 AVLTreeNode
class MyAVLTreeNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.height = 1;
}
// @Override toString 2018-11-24-jwl
toString() {
return this.key + '--->' + this.value + '--->' + this.height;
}
}
// 自定义AVL树 AVLTree
class MyAVLTree {
constructor() {
this.root = null;
this.size = 0;
}
// 比较的功能
compare(keyA, keyB) {
if (keyA === null || keyB === null)
throw new Error("key is error. key can't compare.");
if (keyA > keyB) return 1;
else if (keyA < keyB) return -1;
else return 0;
}
// 获取某个节点的高度 -
getHeight(node) {
// 节点为空 返回0
if (!node) return 0;
// 直接返回这个节点的高度
return node.height;
}
// 获取一个节点的平衡因子 -
getBalanceFactor(node) {
// 节点为空 返回0
if (!node) return 0;
// 左右子树的高度值
const leftHeight = this.getHeight(node.left);
const rightHeight = this.getHeight(node.right);
// 左子树的高度 - 右子树高度的值 = 平衡因子
return leftHeight - rightHeight;
}
// 根据key获取节点 -
getNode(node, key) {
// 先解决最基本的问题
if (node === null) return null;
// 开始将复杂的问题 逐渐缩小规模
// 从而求出小问题的解,最后构建出原问题的解
switch (this.compare(node.key, key)) {
case 1: // 向左找
return this.getNode(node.left, key);
break;
case -1: // 向右找
return this.getNode(node.right, key);
break;
case 0: // 找到了
return node;
break;
default:
throw new Error(
'compare result is error. compare result : 0、 一、 -1 .'
);
break;
}
}
// 添加操做 +
add(key, value) {
this.root = this.recursiveAdd(this.root, key, value);
}
// 添加操做 递归算法 -
recursiveAdd(node, key, value) {
// 解决最简单的问题
if (node === null) {
this.size++;
return new MyAVLTreeNode(key, value);
}
// 将复杂的问题规模逐渐变小,
// 从而求出小问题的解,从而构建出原问题的答案
if (this.compare(node.key, key) > 0)
node.left = this.recursiveAdd(node.left, key, value);
else if (this.compare(node.key, key) < 0)
node.right = this.recursiveAdd(node.right, key, value);
else node.value = value;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
node.height =
1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
// // 计算一个节点的平衡因子
// const balanceFactor = this.getBalanceFactor(node);
// // 若是平衡因子的绝对值大于1 说明不知足AVL平衡二叉树的性质了
// if (Math.abs(balanceFactor) > 1) {
// console.log(node.toString() + " unbalanced : " + balanceFactor + "\r\n");
// document.body.innerHTML += node.toString() + " unbalanced : " + balanceFactor + "<br/>";
// }
return node;
}
// 删除操做 返回被删除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 删除操做 递归算法 +
recursiveRemove(node, key) {
// 解决最基本的问题
if (node === null) return null;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
return node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
return node;
} else {
// 当前节点的key 与 待删除的key的那个节点相同
// 有三种状况
// 1. 当前节点没有左子树,那么只有让当前节点的右子树直接覆盖当前节点,就表示当前节点被删除了
// 2. 当前节点没有右子树,那么只有让当前节点的左子树直接覆盖当前节点,就表示当前节点被删除了
// 3. 当前节点左右子树都有, 那么又分两种状况,使用前驱删除法或者后继删除法
// 1. 前驱删除法:使用当前节点的左子树上最大的那个节点覆盖当前节点
// 2. 后继删除法:使用当前节点的右子树上最小的那个节点覆盖当前节点
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
} else if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left);
this.size++;
// 开始嫁接 当前节点的左右子树
predecessor.left = node.left;
predecessor.right = node.right;
// 将当前节点从根节点剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接后的新节点
return predecessor;
}
}
}
// 删除操做的两个辅助函数
// 获取最大值、删除最大值
// 之前驱的方式 来辅助删除操做的函数
// 获取最大值
maximum(node) {
// 不再能往右了,说明当前节点已是最大的了
if (node.right === null) return node;
// 将复杂的问题渐渐减少规模,从而求出小问题的解,最后用小问题的解构建出原问题的答案
return this.maximum(node.right);
}
// 删除最大值
removeMax(node) {
// 解决最基本的问题
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 开始化归
node.right = this.removeMax(node.right);
return node;
}
// 查询操做 返回查询到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含该key的元素的判断值 +
contains(key) {
return this.getNode(this.root, key) !== null;
}
// 返回映射中实际的元素个数 +
getSize() {
return this.size;
}
// 返回映射中是否为空的判断值 +
isEmpty() {
return this.size === 0;
}
// 判断当前这棵树是不是一棵二分搜索树,有二分搜索树顺序性
isBanarySearchTree() {
// 若是节点为空 那么这就是一棵空的二分搜索树
if (!this.root) return true;
// 存储二分搜索树中的key
const list = new Array();
// 中序遍历后,添加到list中的值会是以从小到大升序的样子排列
this.inOrder(this.root, list);
// 从前日后判断 list中的值是不是从小到大升序的排列
// 验证 当前树是否符合二分搜索树的性质
for (var i = 1; i < list.length; i++)
if (list[i - 1] > list[i]) return false;
return true;
}
// 中序遍历 辅助函数 -
inOrder(node, list) {
// 递归到底的状况
if (!node) return;
// 中序遍历时,添加到数组中的值会是以从小到大升序的样子排列
this.inOrder(node.left, list);
list.push(node.key);
this.inOrder(node.right, list);
}
// 判断该二叉树是否一棵平衡二叉树
isBalanced() {
return this.recursiveIsBalanced(this.root);
}
// 递归判断某一个节点是否符合平衡二叉树的定义 辅助函数 -
recursiveIsBalanced(node) {
// 可以递归到底,说明符合要求
// 空的节点左右孩子高度差确定为0,
// 由于空树没有左右子树,更加谈不上下面去判断它的左右子树高度差是否会超过一。
if (!node) return true;
// 若是当前节点的高度差大于1 说明不符合要求
if (Math.abs(this.getBalanceFactor(node)) > 1) return false;
// 递归的去判断当前节点的 左右子树是否符合要求
return (
this.recursiveIsBalanced(node.left) &&
this.recursiveIsBalanced(node.right)
);
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyBinarySearchTreeMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyBinarySearchTreeMap: size = ${ this.size }, data = [ <br/><br/>`;
// 以非递归的前序遍历 输出字符串
let stack = new MyLinkedListStack();
stack.push(this.root);
if (this.root === null) stack.pop();
while (!stack.isEmpty()) {
let node = stack.pop();
if (node.left !== null) stack.push(node.left);
if (node.right !== null) stack.push(node.right);
if (node.left === null && node.right === null) {
mapInfo += ` ${node.toString()} \r\n`;
document.body.innerHTML += ` ${node.toString()} <br/><br/>`;
} else {
mapInfo += ` ${node.toString()}, \r\n`;
document.body.innerHTML += ` ${node.toString()}, <br/><br/>`;
}
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
复制代码
Main
// main 函数
class Main {
constructor() {
this.alterLine('AVLTree Area');
// 千级别
const openCount = 100; // 操做数
const rank = 10000000;
// 生成同一份测试数据的辅助代码
const random = Math.random;
const array = new Array(openCount);
// 生成同一份测试数据
for (var i = 0; i < openCount; i++)
array[i] = Math.floor(random() * rank);
// 建立AVL树实例
const avl = new MyAVLTree();
for (const value of array) avl.add(value);
// 输出当前这棵avl树是不是一个二分搜索树
this.show('Is Binary Search Tree : ' + avl.isBanarySearchTree());
console.log('Is Binary Search Tree : ' + avl.isBanarySearchTree());
// 输出当前这棵avl树是不是一个平衡二叉树
this.show('Is Balanced : ' + avl.isBalanced());
console.log('Is Balanced : ' + avl.isBalanced());
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
// 页面加载完毕
window.onload = function() {
// 执行主函数
new Main();
};
复制代码
AVL 树是经过两个主要的机制来实现自平衡的
AVL 树维护自平衡在何时发生
大于1
或者小于-1
,AVL 树维护平衡性的原理
右旋转操做
// 最开始这棵树是这种状况 T1 < Z < T2 < X < T3 < Y < T4
// (Y)
// / \
// (X) (T4)
// / \
// (Z) (T3)
// / \
// (T1) (T2)
// 右旋转后是这样子,依然是T1 < Z < T2 < X < T3 < Y < T4
// (X)
// / \
// (Z) (Y)
// / \ / \
// (T1) (T2)(T3)(T4)
复制代码
// 自定义AVL树节点 AVLTreeNode
class MyAVLTreeNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.height = 1;
}
// @Override toString 2018-11-24-jwl
toString() {
return this.key + '--->' + this.value + '--->' + this.height;
}
}
// 自定义AVL树 AVLTree
class MyAVLTree {
constructor() {
this.root = null;
this.size = 0;
}
// 比较的功能
compare(keyA, keyB) {
if (keyA === null || keyB === null)
throw new Error("key is error. key can't compare.");
if (keyA > keyB) return 1;
else if (keyA < keyB) return -1;
else return 0;
}
// 获取某个节点的高度 -
getHeight(node) {
// 节点为空 返回0
if (!node) return 0;
// 直接返回这个节点的高度
return node.height;
}
// 获取一个节点的平衡因子 -
getBalanceFactor(node) {
// 节点为空 返回0
if (!node) return 0;
// 左右子树的高度值
const leftHeight = this.getHeight(node.left);
const rightHeight = this.getHeight(node.right);
// 左子树的高度 - 右子树高度的值 = 平衡因子
return leftHeight - rightHeight;
}
// 根据key获取节点 -
getNode(node, key) {
// 先解决最基本的问题
if (node === null) return null;
// 开始将复杂的问题 逐渐缩小规模
// 从而求出小问题的解,最后构建出原问题的解
switch (this.compare(node.key, key)) {
case 1: // 向左找
return this.getNode(node.left, key);
break;
case -1: // 向右找
return this.getNode(node.right, key);
break;
case 0: // 找到了
return node;
break;
default:
throw new Error(
'compare result is error. compare result : 0、 一、 -1 .'
);
break;
}
}
// 添加操做 +
add(key, value) {
this.root = this.recursiveAdd(this.root, key, value);
}
// 添加操做 递归算法 -
recursiveAdd(node, key, value) {
// 解决最简单的问题
if (node === null) {
this.size++;
return new MyAVLTreeNode(key, value);
}
// 将复杂的问题规模逐渐变小,
// 从而求出小问题的解,从而构建出原问题的答案
if (this.compare(node.key, key) > 0)
node.left = this.recursiveAdd(node.left, key, value);
else if (this.compare(node.key, key) < 0)
node.right = this.recursiveAdd(node.right, key, value);
else node.value = value;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
node.height =
1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(node);
// 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(node.left) >= 0) {
}
// // 若是平衡因子的绝对值大于1 说明不知足AVL平衡二叉树的性质了
// if (Math.abs(balanceFactor) > 1) {
// console.log(node.toString() + " unbalanced : " + balanceFactor + "\r\n");
// document.body.innerHTML += node.toString() + " unbalanced : " + balanceFactor + "<br/>";
// }
return node;
}
// 删除操做 返回被删除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 删除操做 递归算法 +
recursiveRemove(node, key) {
// 解决最基本的问题
if (node === null) return null;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
return node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
return node;
} else {
// 当前节点的key 与 待删除的key的那个节点相同
// 有三种状况
// 1. 当前节点没有左子树,那么只有让当前节点的右子树直接覆盖当前节点,就表示当前节点被删除了
// 2. 当前节点没有右子树,那么只有让当前节点的左子树直接覆盖当前节点,就表示当前节点被删除了
// 3. 当前节点左右子树都有, 那么又分两种状况,使用前驱删除法或者后继删除法
// 1. 前驱删除法:使用当前节点的左子树上最大的那个节点覆盖当前节点
// 2. 后继删除法:使用当前节点的右子树上最小的那个节点覆盖当前节点
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
} else if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left);
this.size++;
// 开始嫁接 当前节点的左右子树
predecessor.left = node.left;
predecessor.right = node.right;
// 将当前节点从根节点剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接后的新节点
return predecessor;
}
}
}
// 删除操做的两个辅助函数
// 获取最大值、删除最大值
// 之前驱的方式 来辅助删除操做的函数
// 获取最大值
maximum(node) {
// 不再能往右了,说明当前节点已是最大的了
if (node.right === null) return node;
// 将复杂的问题渐渐减少规模,从而求出小问题的解,最后用小问题的解构建出原问题的答案
return this.maximum(node.right);
}
// 删除最大值
removeMax(node) {
// 解决最基本的问题
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 开始化归
node.right = this.removeMax(node.right);
return node;
}
// 查询操做 返回查询到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含该key的元素的判断值 +
contains(key) {
return this.getNode(this.root, key) !== null;
}
// 返回映射中实际的元素个数 +
getSize() {
return this.size;
}
// 返回映射中是否为空的判断值 +
isEmpty() {
return this.size === 0;
}
// 判断当前这棵树是不是一棵二分搜索树,有二分搜索树顺序性
isBanarySearchTree() {
// 若是节点为空 那么这就是一棵空的二分搜索树
if (!this.root) return true;
// 存储二分搜索树中的key
const list = new Array();
// 中序遍历后,添加到list中的值会是以从小到大升序的样子排列
this.inOrder(this.root, list);
// 从前日后判断 list中的值是不是从小到大升序的排列
// 验证 当前树是否符合二分搜索树的性质
for (var i = 1; i < list.length; i++)
if (list[i - 1] > list[i]) return false;
return true;
}
// 中序遍历 辅助函数 -
inOrder(node, list) {
// 递归到底的状况
if (!node) return;
// 中序遍历时,添加到数组中的值会是以从小到大升序的样子排列
this.inOrder(node.left, list);
list.push(node.key);
this.inOrder(node.right, list);
}
// 判断该二叉树是否一棵平衡二叉树
isBalanced() {
return this.recursiveIsBalanced(this.root);
}
// 递归判断某一个节点是否符合平衡二叉树的定义 辅助函数 -
recursiveIsBalanced(node) {
// 可以递归到底,说明符合要求
// 空的节点左右孩子高度差确定为0,
// 由于空树没有左右子树,更加谈不上下面去判断它的左右子树高度差是否会超过一。
if (!node) return true;
// 若是当前节点的高度差大于1 说明不符合要求
if (Math.abs(this.getBalanceFactor(node)) > 1) return false;
// 递归的去判断当前节点的 左右子树是否符合要求
return (
this.recursiveIsBalanced(node.left) &&
this.recursiveIsBalanced(node.right)
);
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyBinarySearchTreeMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyBinarySearchTreeMap: size = ${ this.size }, data = [ <br/><br/>`;
// 以非递归的前序遍历 输出字符串
let stack = new MyLinkedListStack();
stack.push(this.root);
if (this.root === null) stack.pop();
while (!stack.isEmpty()) {
let node = stack.pop();
if (node.left !== null) stack.push(node.left);
if (node.right !== null) stack.push(node.right);
if (node.left === null && node.right === null) {
mapInfo += ` ${node.toString()} \r\n`;
document.body.innerHTML += ` ${node.toString()} <br/><br/>`;
} else {
mapInfo += ` ${node.toString()}, \r\n`;
document.body.innerHTML += ` ${node.toString()}, <br/><br/>`;
}
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
复制代码
// main 函数
class Main {
constructor() {
this.alterLine('AVLTree Area');
// 千级别
const openCount = 100; // 操做数
const rank = 10000000;
// 生成同一份测试数据的辅助代码
const random = Math.random;
const array = new Array(openCount);
// 生成同一份测试数据
for (var i = 0; i < openCount; i++)
array[i] = Math.floor(random() * rank);
// 建立AVL树实例
const avl = new MyAVLTree();
for (const value of array) avl.add(value);
// 输出当前这棵avl树是不是一个二分搜索树
this.show('Is Binary Search Tree : ' + avl.isBanarySearchTree());
console.log('Is Binary Search Tree : ' + avl.isBanarySearchTree());
// 输出当前这棵avl树是不是一个平衡二叉树
this.show('Is Balanced : ' + avl.isBalanced());
console.log('Is Balanced : ' + avl.isBalanced());
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
// 页面加载完毕
window.onload = function() {
// 执行主函数
new Main();
};
复制代码
右旋转
// 对节点y进行向右旋转操做,返回旋转后新的根节点x
// y x
// / \ / \
// x T4 向右旋转 (y) z y
// / \ - - - - - - - -> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
复制代码
左旋转
// 最开始这棵树是这种状况 T4 < Y < T3 < X < T1 < Z < T2
// (Y)
// / \
// (T4) (X)
// / \
// (T3) (Z)
// / \
// (T1) (T2)
// 左旋转后是这样子,依然是T4 < Y < T3 < X < T1 < Z < T2
// (X)
// / \
// (Y) (Z)
// / \ / \
// (T4)(T3)(T1)(T2)
复制代码
不管是左旋转仍是右旋转都是一种状况
使用了左旋转和右旋转来进行平衡性的维护以后
AVLTree
// 自定义AVL树节点 AVLTreeNode
class MyAVLTreeNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.height = 1;
}
// @Override toString 2018-11-24-jwl
toString() {
return this.key + '--->' + this.value + '--->' + this.height;
}
}
// 自定义AVL树 AVLTree
class MyAVLTree {
constructor() {
this.root = null;
this.size = 0;
}
// 比较的功能
compare(keyA, keyB) {
if (keyA === null || keyB === null)
throw new Error("key is error. key can't compare.");
if (keyA > keyB) return 1;
else if (keyA < keyB) return -1;
else return 0;
}
// 获取某个节点的高度 -
getHeight(node) {
// 节点为空 返回0
if (!node) return 0;
// 直接返回这个节点的高度
return node.height;
}
// 获取一个节点的平衡因子 -
getBalanceFactor(node) {
// 节点为空 返回0
if (!node) return 0;
// 左右子树的高度值
const leftHeight = this.getHeight(node.left);
const rightHeight = this.getHeight(node.right);
// 左子树的高度 - 右子树高度的值 = 平衡因子
return leftHeight - rightHeight;
}
// 根据key获取节点 -
getNode(node, key) {
// 先解决最基本的问题
if (node === null) return null;
// 开始将复杂的问题 逐渐缩小规模
// 从而求出小问题的解,最后构建出原问题的解
switch (this.compare(node.key, key)) {
case 1: // 向左找
return this.getNode(node.left, key);
break;
case -1: // 向右找
return this.getNode(node.right, key);
break;
case 0: // 找到了
return node;
break;
default:
throw new Error(
'compare result is error. compare result : 0、 一、 -1 .'
);
break;
}
}
// 对节点y进行向右旋转操做,返回旋转后新的根节点x
// y x
// / \ / \
// x T4 向右旋转 (y) z y
// / \ - - - - - - - -> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
rightRotate(y) {
const x = y.left;
const T3 = x.right;
// 向右旋转的过程
y.left = T3;
x.right = y;
// 更新节点的height值 只须要更新x和y的便可
y.height =
1 + Math.max(this.getHeight(y.left), this.getHeight(y.right));
x.height =
1 + Math.max(this.getHeight(x.left), this.getHeight(x.right));
// 返回 新节点 x
return x;
}
// 对节点y进行向左旋转操做,返回旋转后新的根节点x
// y x
// / \ / \
// T1 x 向左旋转 (y) y z
// / \ - - - - - - - -> / \ / \
// T2 z T1 T2 T3 T4
// / \
// T3 T4
leftRotate(y) {
const x = y.right;
const T2 = x.left;
// 向左旋转的过程
y.right = T2;
x.left = y;
// 更新节点的height值 只须要更新x和y的便可
y.height =
1 + Math.max(this.getHeight(y.left), this.getHeight(y.right));
x.height =
1 + Math.max(this.getHeight(x.left), this.getHeight(x.right));
// 返回 新节点 x
return x;
}
// 添加操做 +
add(key, value) {
this.root = this.recursiveAdd(this.root, key, value);
}
// 添加操做 递归算法 -
recursiveAdd(node, key, value) {
// 解决最简单的问题
if (node === null) {
this.size++;
return new MyAVLTreeNode(key, value);
}
// 将复杂的问题规模逐渐变小,
// 从而求出小问题的解,从而构建出原问题的答案
if (this.compare(node.key, key) > 0)
node.left = this.recursiveAdd(node.left, key, value);
else if (this.compare(node.key, key) < 0)
node.right = this.recursiveAdd(node.right, key, value);
else node.value = value;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
node.height =
1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(node);
// 若是平衡因子的绝对值大于1 说明不知足AVL平衡二叉树的性质了
if (Math.abs(balanceFactor) > 1) {
console.log(
node.toString() + ' unbalanced : ' + balanceFactor + '\r\n'
);
document.body.innerHTML +=
node.toString() + ' unbalanced : ' + balanceFactor + '<br/>';
}
// 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(node.left) >= 0)
return this.rightRotate(node);
// 平衡维护 右旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (balanceFactor < -1 && this.getBalanceFactor(node.left) <= 0)
return this.leftRotate(node);
return node;
}
// 删除操做 返回被删除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 删除操做 递归算法 +
recursiveRemove(node, key) {
// 解决最基本的问题
if (node === null) return null;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
return node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
return node;
} else {
// 当前节点的key 与 待删除的key的那个节点相同
// 有三种状况
// 1. 当前节点没有左子树,那么只有让当前节点的右子树直接覆盖当前节点,就表示当前节点被删除了
// 2. 当前节点没有右子树,那么只有让当前节点的左子树直接覆盖当前节点,就表示当前节点被删除了
// 3. 当前节点左右子树都有, 那么又分两种状况,使用前驱删除法或者后继删除法
// 1. 前驱删除法:使用当前节点的左子树上最大的那个节点覆盖当前节点
// 2. 后继删除法:使用当前节点的右子树上最小的那个节点覆盖当前节点
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
} else if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left);
this.size++;
// 开始嫁接 当前节点的左右子树
predecessor.left = node.left;
predecessor.right = node.right;
// 将当前节点从根节点剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接后的新节点
return predecessor;
}
}
}
// 删除操做的两个辅助函数
// 获取最大值、删除最大值
// 之前驱的方式 来辅助删除操做的函数
// 获取最大值
maximum(node) {
// 不再能往右了,说明当前节点已是最大的了
if (node.right === null) return node;
// 将复杂的问题渐渐减少规模,从而求出小问题的解,最后用小问题的解构建出原问题的答案
return this.maximum(node.right);
}
// 删除最大值
removeMax(node) {
// 解决最基本的问题
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 开始化归
node.right = this.removeMax(node.right);
return node;
}
// 查询操做 返回查询到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含该key的元素的判断值 +
contains(key) {
return this.getNode(this.root, key) !== null;
}
// 返回映射中实际的元素个数 +
getSize() {
return this.size;
}
// 返回映射中是否为空的判断值 +
isEmpty() {
return this.size === 0;
}
// 判断当前这棵树是不是一棵二分搜索树,有二分搜索树顺序性
isBanarySearchTree() {
// 若是节点为空 那么这就是一棵空的二分搜索树
if (!this.root) return true;
// 存储二分搜索树中的key
const list = new Array();
// 中序遍历后,添加到list中的值会是以从小到大升序的样子排列
this.inOrder(this.root, list);
// 从前日后判断 list中的值是不是从小到大升序的排列
// 验证 当前树是否符合二分搜索树的性质
for (var i = 1; i < list.length; i++)
if (list[i - 1] > list[i]) return false;
return true;
}
// 中序遍历 辅助函数 -
inOrder(node, list) {
// 递归到底的状况
if (!node) return;
// 中序遍历时,添加到数组中的值会是以从小到大升序的样子排列
this.inOrder(node.left, list);
list.push(node.key);
this.inOrder(node.right, list);
}
// 判断该二叉树是否一棵平衡二叉树
isBalanced() {
return this.recursiveIsBalanced(this.root);
}
// 递归判断某一个节点是否符合平衡二叉树的定义 辅助函数 -
recursiveIsBalanced(node) {
// 可以递归到底,说明符合要求
// 空的节点左右孩子高度差确定为0,
// 由于空树没有左右子树,更加谈不上下面去判断它的左右子树高度差是否会超过一。
if (!node) return true;
// 若是当前节点的高度差大于1 说明不符合要求
if (Math.abs(this.getBalanceFactor(node)) > 1) return false;
// 递归的去判断当前节点的 左右子树是否符合要求
return (
this.recursiveIsBalanced(node.left) &&
this.recursiveIsBalanced(node.right)
);
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyBinarySearchTreeMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyBinarySearchTreeMap: size = ${ this.size }, data = [ <br/><br/>`;
// 以非递归的前序遍历 输出字符串
let stack = new MyLinkedListStack();
stack.push(this.root);
if (this.root === null) stack.pop();
while (!stack.isEmpty()) {
let node = stack.pop();
if (node.left !== null) stack.push(node.left);
if (node.right !== null) stack.push(node.right);
if (node.left === null && node.right === null) {
mapInfo += ` ${node.toString()} \r\n`;
document.body.innerHTML += ` ${node.toString()} <br/><br/>`;
} else {
mapInfo += ` ${node.toString()}, \r\n`;
document.body.innerHTML += ` ${node.toString()}, <br/><br/>`;
}
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
复制代码
Main
// main 函数
class Main {
constructor() {
this.alterLine('AVLTree Area');
// 千级别
const openCount = 100; // 操做数
const rank = 10000000;
// 生成同一份测试数据的辅助代码
const random = Math.random;
const array = new Array(openCount);
// 生成同一份测试数据
for (var i = 0; i < openCount; i++)
array[i] = Math.floor(random() * rank);
// 建立AVL树实例
const avl = new MyAVLTree();
for (const value of array) avl.add(value);
// 输出当前这棵avl树是不是一个二分搜索树
this.show('Is Binary Search Tree : ' + avl.isBanarySearchTree());
console.log('Is Binary Search Tree : ' + avl.isBanarySearchTree());
// 输出当前这棵avl树是不是一个平衡二叉树
this.show('Is Balanced : ' + avl.isBalanced());
console.log('Is Balanced : ' + avl.isBalanced());
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
// 页面加载完毕
window.onload = function() {
// 执行主函数
new Main();
};
复制代码