【从蛋壳到满天飞】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
左旋转和右旋转的状况算法
其它两种状况api
不一样状况的区分数组
一共分为 4 种状况,LL、RR、LR、RL,数据结构
LL 表示的是插入的元素在不平衡节点的左侧的左侧的时候,dom
RR 表示的是插入的元素在不平衡节点的右侧的右侧的时候,ide
LR 表示的是插入的元素在不平衡节点的左侧的右侧的时候,
RL 表示的是插入的元素在不平衡节点的右侧的左侧的时候。
// RR的状况 新插入的节点在Y的右侧的右侧
// (Y)
// / \
// (T4) (X)
// / \
// (T3) (Z)
// / \
// (T1) (T2)
// LL的状况 新插入的节点在Y的左侧的左侧
// (Y)
// / \
// (X) (T4)
// / \
// (Z) (T3)
// / \
// (T1) (T2)
// LR的状况 新插入的节点在Y的左侧的右侧
// (Y)
// / \
// (X) (T4)
// / \
// (T1) (Z)
// / \
// (T2)(T3)
// RL的状况 新插入的节点在Y的右侧的左侧
// (Y)
// / \
// (T1) (X)
// / \
// (Z) (T4)
// / \
// (T2)(T3)
复制代码
LR 的处理方式
// LR的状况 新插入的节点在Y的左侧的右侧
// (Y)
// / \
// (X) (T4)
// / \
// (T1) (Z)
// / \
// (T2)(T3)
// 对X节点进行左旋转,就将LR转换为了LL的状况
// (Y)
// / \
// (Z) (T4)
// / \
// (X) (T3)
// / \
// (T1) (T2)
复制代码
RL 的处理方式
// RL的状况 新插入的节点在Y的右侧的左侧
// (Y)
// / \
// (T1) (X)
// / \
// (Z) (T4)
// / \
// (T2)(T3)
// 对X节点进行右旋转,就将LR转换为了RR的状况
// (Y)
// / \
// (T1) (Z)
// / \
// (T2) (X)
// / \
// (T3)(T4)
复制代码
已经对 LL 和 RR 这两种状况进行了处理
对 LL、RR、LR、RL 这四种状况进行了处理
将二分搜树改形成了 AVL 树
O(logn)
这个级别的,性能测试
O(n)
级别的了,O(logn)
这个级别的,(class: MyBSTMap, class: AVLTree, class: PerformanceTest, class: Main)
MyBSTMap
// 自定义二分搜索树树映射节点 TreeMapNode
class MyBinarySearchTreeMapNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
// @Override toString 2018-11-5-jwl
toString() {
return this.key.toString() + '---------->' + this.value.toString();
}
}
// 自定义二分搜索树映射 Map
class MyBinarySearchTreeMap {
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;
}
// 根据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 MyBinarySearchTreeMapNode(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;
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;
}
}
复制代码
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/>';
}
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(node.left) >= 0)
return this.rightRotate(node);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (balanceFactor < -1 && this.getBalanceFactor(node.right) <= 0)
return this.leftRotate(node);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (balanceFactor > 1 && this.getBalanceFactor(node.left) < 0) {
node.left = this.leftRotate(node.left);
return this.rightRotate(node);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (balanceFactor < -1 && this.getBalanceFactor(node.right) > 0) {
node.right = this.rightRotate(node.right);
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;
}
}
复制代码
PerformanceTest
// 性能测试
class PerformanceTest {
constructor() {}
// 对比队列
testQueue(queue, openCount) {
let startTime = Date.now();
let random = Math.random;
for (var i = 0; i < openCount; i++) {
queue.enqueue(random() * openCount);
}
while (!queue.isEmpty()) {
queue.dequeue();
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 对比栈
testStack(stack, openCount) {
let startTime = Date.now();
let random = Math.random;
for (var i = 0; i < openCount; i++) {
stack.push(random() * openCount);
}
while (!stack.isEmpty()) {
stack.pop();
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 对比集合
testSet(set, openCount) {
let startTime = Date.now();
let random = Math.random;
let arr = [];
let temp = null;
// 第一遍测试
for (var i = 0; i < openCount; i++) {
temp = random();
// 添加剧复元素,从而测试集合去重的能力
set.add(temp * openCount);
set.add(temp * openCount);
arr.push(temp * openCount);
}
for (var i = 0; i < openCount; i++) {
set.remove(arr[i]);
}
// 第二遍测试
for (var i = 0; i < openCount; i++) {
set.add(arr[i]);
set.add(arr[i]);
}
while (!set.isEmpty()) {
set.remove(arr[set.getSize() - 1]);
}
let endTime = Date.now();
// 求出两次测试的平均时间
let avgTime = Math.ceil((endTime - startTime) / 2);
return this.calcTime(avgTime);
}
// 对比映射
testMap(map, openCount) {
let startTime = Date.now();
let array = new MyArray();
let random = Math.random;
let temp = null;
let result = null;
for (var i = 0; i < openCount; i++) {
temp = random();
result = openCount * temp;
array.add(result);
array.add(result);
array.add(result);
array.add(result);
}
for (var i = 0; i < array.getSize(); i++) {
result = array.get(i);
if (map.contains(result)) map.add(result, map.get(result) + 1);
else map.add(result, 1);
}
for (var i = 0; i < array.getSize(); i++) {
result = array.get(i);
map.remove(result);
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 对比堆 主要对比 使用heapify 与 不使用heapify时的性能
testHeap(heap, array, isHeapify) {
const startTime = Date.now();
// 是否支持 heapify
if (isHeapify) heap.heapify(array);
else {
for (const element of array) heap.add(element);
}
console.log('heap size:' + heap.size() + '\r\n');
document.body.innerHTML += 'heap size:' + heap.size() + '<br /><br />';
// 使用数组取值
let arr = new Array(heap.size());
for (let i = 0; i < arr.length; i++) arr[i] = heap.extractMax();
console.log(
'Array size:' + arr.length + ',heap size:' + heap.size() + '\r\n'
);
document.body.innerHTML +=
'Array size:' +
arr.length +
',heap size:' +
heap.size() +
'<br /><br />';
// 检验一下是否符合要求
for (let i = 1; i < arr.length; i++)
if (arr[i - 1] < arr[i]) throw new Error('error.');
console.log('test heap completed.' + '\r\n');
document.body.innerHTML += 'test heap completed.' + '<br /><br />';
const endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 对比并查集
testUnionFind(unionFind, openCount, primaryArray, secondaryArray) {
const size = unionFind.getSize();
const random = Math.random;
return this.testCustomFn(function() {
// 合并操做
for (var i = 0; i < openCount; i++) {
let primaryId = primaryArray[i];
let secondaryId = secondaryArray[i];
unionFind.unionElements(primaryId, secondaryId);
}
// 查询链接操做
for (var i = 0; i < openCount; i++) {
let primaryRandomId = Math.floor(random() * size);
let secondaryRandomId = Math.floor(random() * size);
unionFind.unionElements(primaryRandomId, secondaryRandomId);
}
});
}
// 计算运行的时间,转换为 天-小时-分钟-秒-毫秒
calcTime(result) {
//获取距离的天数
var day = Math.floor(result / (24 * 60 * 60 * 1000));
//获取距离的小时数
var hours = Math.floor((result / (60 * 60 * 1000)) % 24);
//获取距离的分钟数
var minutes = Math.floor((result / (60 * 1000)) % 60);
//获取距离的秒数
var seconds = Math.floor((result / 1000) % 60);
//获取距离的毫秒数
var milliSeconds = Math.floor(result % 1000);
// 计算时间
day = day < 10 ? '0' + day : day;
hours = hours < 10 ? '0' + hours : hours;
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
milliSeconds =
milliSeconds < 100
? milliSeconds < 10
? '00' + milliSeconds
: '0' + milliSeconds
: milliSeconds;
// 输出耗时字符串
result =
day +
'天' +
hours +
'小时' +
minutes +
'分' +
seconds +
'秒' +
milliSeconds +
'毫秒' +
' <<<<============>>>> 总毫秒数:' +
result;
return result;
}
// 自定义对比
testCustomFn(fn) {
let startTime = Date.now();
fn();
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
}
复制代码
Main
// main 函数
class Main {
constructor() {
this.alterLine('Map Comparison Area');
const n = 2000000;
// const n = 200;
const myBSTMap = new MyBinarySearchTreeMap();
const myAVLTree = new MyAVLTree();
let performanceTest1 = new PerformanceTest();
const random = Math.random;
let arrNumber = new Array(n);
// 循环添加随机数的值
for (let i = 0; i < n; i++) arrNumber[i] = Math.floor(n * random());
this.alterLine('MyBSTMap Comparison Area');
const myBSTMapInfo = performanceTest1.testCustomFn(function() {
// 添加
for (const word of arrNumber)
myBSTMap.add(word, String.fromCharCode(word));
// 删除
for (const word of arrNumber) myBSTMap.remove(word);
// 查找
for (const word of arrNumber)
if (myBSTMap.contains(word))
throw new Error("doesn't remove ok.");
});
// 总毫秒数:
console.log(myBSTMapInfo);
console.log(myBSTMap);
this.show(myBSTMapInfo);
this.alterLine('MyAVLTree Comparison Area');
const that = this;
const myAVLTreeInfo = performanceTest1.testCustomFn(function() {
for (const word of arrNumber)
myAVLTree.add(word, String.fromCharCode(word));
// 输出当前这棵myAVLTree树是不是一个二分搜索树
that.show(
'Is Binary Search Tree : ' + myAVLTree.isBanarySearchTree()
);
console.log(
'Is Binary Search Tree : ' + myAVLTree.isBanarySearchTree()
);
// 输出当前这棵myAVLTree树是不是一个平衡二叉树
that.show('Is Balanced : ' + myAVLTree.isBalanced());
console.log('Is Balanced : ' + myAVLTree.isBalanced());
// 删除
for (const word of arrNumber) {
myAVLTree.remove(word);
}
// // 查找
for (const word of arrNumber)
if (myAVLTree.contains(word))
throw new Error("doesn't remove ok.");
});
console.log(myAVLTree);
// 总毫秒数:
console.log(myAVLTreeInfo);
this.show(myAVLTreeInfo);
}
// 将内容显示在页面上
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) 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/>";
// }
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(node.left) >= 0)
return this.rightRotate(node);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (balanceFactor < -1 && this.getBalanceFactor(node.right) <= 0)
return this.leftRotate(node);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (balanceFactor > 1 && this.getBalanceFactor(node.left) < 0) {
node.left = this.leftRotate(node.left);
return this.rightRotate(node);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (balanceFactor < -1 && this.getBalanceFactor(node.right) > 0) {
node.right = this.rightRotate(node.right);
return this.leftRotate(node);
}
return node;
}
// 删除操做 返回被删除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (!node) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 删除操做 递归算法 +
recursiveRemove(node, key) {
// 解决最基本的问题
if (!node) return null;
// 临时存储待返回的节点,可是返回以前先对它的平衡进行一下维护。
let returnNode;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
returnNode = node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
returnNode = node;
} else {
// 当前节点的key 与 待删除的key的那个节点相同
// 有三种状况
// 1. 当前节点没有左子树,那么只有让当前节点的右子树直接覆盖当前节点,就表示当前节点被删除了
// 2. 当前节点没有右子树,那么只有让当前节点的左子树直接覆盖当前节点,就表示当前节点被删除了
// 3. 当前节点左右子树都有, 那么又分两种状况,使用前驱删除法或者后继删除法
// 1. 前驱删除法:使用当前节点的左子树上最大的那个节点覆盖当前节点
// 2. 后继删除法:使用当前节点的右子树上最小的那个节点覆盖当前节点
if (!node.left) {
let rightNode = node.right;
node.right = null;
this.size--;
returnNode = rightNode;
} else if (!node.right) {
let leftNode = node.left;
node.left = null;
this.size--;
returnNode = leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left); // this.recursiveRemove(node.left, predecessor.key)
this.size++;
// 开始嫁接 当前节点的左右子树
predecessor.left = node.left;
predecessor.right = node.right;
// 将当前节点从根节点剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接后的新节点
returnNode = predecessor;
}
}
// 若是本来的节点或者新的节点是空 直接返回空便可 不须要下面的平衡维护
if (!returnNode) return null;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
returnNode.height =
1 +
Math.max(
this.getHeight(returnNode.left),
this.getHeight(returnNode.right)
);
// 删除节点后进行节点的平衡维护
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(returnNode);
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(returnNode.left) >= 0)
return this.rightRotate(returnNode);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (balanceFactor < -1 && this.getBalanceFactor(returnNode.right) <= 0)
return this.leftRotate(returnNode);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (balanceFactor > 1 && this.getBalanceFactor(returnNode.left) < 0) {
returnNode.left = this.leftRotate(returnNode.left);
return this.rightRotate(returnNode);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (
balanceFactor < -1 &&
this.getBalanceFactor(returnNode.right) > 0
) {
returnNode.right = this.rightRotate(returnNode.right);
return this.leftRotate(returnNode);
}
return returnNode;
}
// 删除操做的两个辅助函数
// 获取最大值、删除最大值
// 之前驱的方式 来辅助删除操做的函数
// 获取最大值
maximum(node) {
// 不再能往右了,说明当前节点已是最大的了
if (!node.right) return node;
// 将复杂的问题渐渐减少规模,从而求出小问题的解,最后用小问题的解构建出原问题的答案
return this.maximum(node.right);
}
// 删除最大值
removeMax(node) {
// 临时存储待返回的节点,可是返回以前先对它的平衡进行一下维护。
let returnNode;
// 解决最基本的问题
if (!node.right) {
let leftNode = node.left;
node.left = null;
this.size--;
returnNode = leftNode;
} else {
// 开始化归
node.right = this.removeMax(node.right);
returnNode = node;
}
// 若是本来的节点或者新的节点是空 直接返回空便可 不须要下面的平衡维护
if (!returnNode) return null;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
returnNode.height =
1 +
Math.max(
this.getHeight(returnNode.left),
this.getHeight(returnNode.right)
);
// 删除节点后进行节点的平衡维护
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(returnNode);
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(returnNode.left) >= 0)
return this.rightRotate(returnNode);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (balanceFactor < -1 && this.getBalanceFactor(returnNode.right) <= 0)
return this.leftRotate(returnNode);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (balanceFactor > 1 && this.getBalanceFactor(returnNode.left) < 0) {
returnNode.left = this.leftRotate(returnNode.left);
return this.rightRotate(returnNode);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (
balanceFactor < -1 &&
this.getBalanceFactor(returnNode.right) > 0
) {
returnNode.right = this.rightRotate(returnNode.right);
return this.leftRotate(returnNode);
}
return returnNode;
}
// 查询操做 返回查询到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (!node) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (!node) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含该key的元素的判断值 +
contains(key) {
return !!this.getNode(this.root, key);
}
// 返回映射中实际的元素个数 +
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;
}
}
复制代码
Olog(n)
这个级别的,O(nlogn)
级别的时间复杂度,O(logn)
这个级别的,MyAVLTree
// 自定义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) 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/>";
// }
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (balanceFactor > 1 && this.getBalanceFactor(node.left) >= 0)
return this.rightRotate(node);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (balanceFactor < -1 && this.getBalanceFactor(node.right) <= 0)
return this.leftRotate(node);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (balanceFactor > 1 && this.getBalanceFactor(node.left) < 0) {
node.left = this.leftRotate(node.left);
return this.rightRotate(node);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (balanceFactor < -1 && this.getBalanceFactor(node.right) > 0) {
node.right = this.rightRotate(node.right);
return this.leftRotate(node);
}
return node;
}
// 删除操做 返回被删除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (!node) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 删除操做 递归算法 +
recursiveRemove(node, key) {
// 解决最基本的问题
if (!node) return null;
// 临时存储待返回的节点,可是返回以前先对它的平衡进行一下维护。
let returnNode;
const originHeight = node.height; // 记录原节点的高度
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
returnNode = node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
returnNode = node;
} else {
// 当前节点的key 与 待删除的key的那个节点相同
// 有三种状况
// 1. 当前节点没有左子树,那么只有让当前节点的右子树直接覆盖当前节点,就表示当前节点被删除了
// 2. 当前节点没有右子树,那么只有让当前节点的左子树直接覆盖当前节点,就表示当前节点被删除了
// 3. 当前节点左右子树都有, 那么又分两种状况,使用前驱删除法或者后继删除法
// 1. 前驱删除法:使用当前节点的左子树上最大的那个节点覆盖当前节点
// 2. 后继删除法:使用当前节点的右子树上最小的那个节点覆盖当前节点
if (!node.left) {
let rightNode = node.right;
node.right = null;
this.size--;
returnNode = rightNode;
} else if (!node.right) {
let leftNode = node.left;
node.left = null;
this.size--;
returnNode = leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left); // this.recursiveRemove(node.left, predecessor.key)
this.size++;
// 开始嫁接 当前节点的左右子树
predecessor.left = node.left;
predecessor.right = node.right;
// 将当前节点从根节点剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接后的新节点
returnNode = predecessor;
}
}
// 若是本来的节点或者新的节点是空 直接返回空便可 不须要下面的平衡维护
if (!returnNode) return null;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
returnNode.height =
1 +
Math.max(
this.getHeight(returnNode.left),
this.getHeight(returnNode.right)
);
// 旧节点的高度若是和新节点的高度一致,就不须要进行节点的平衡维护了
if (originHeight !== returnNode.height) {
// 删除节点后进行节点的平衡维护
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(returnNode);
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (
balanceFactor > 1 &&
this.getBalanceFactor(returnNode.left) >= 0
)
return this.rightRotate(returnNode);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (
balanceFactor < -1 &&
this.getBalanceFactor(returnNode.right) <= 0
)
return this.leftRotate(returnNode);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (
balanceFactor > 1 &&
this.getBalanceFactor(returnNode.left) < 0
) {
returnNode.left = this.leftRotate(returnNode.left);
return this.rightRotate(returnNode);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (
balanceFactor < -1 &&
this.getBalanceFactor(returnNode.right) > 0
) {
returnNode.right = this.rightRotate(returnNode.right);
return this.leftRotate(returnNode);
}
}
return returnNode;
}
// 删除操做的两个辅助函数
// 获取最大值、删除最大值
// 之前驱的方式 来辅助删除操做的函数
// 获取最大值
maximum(node) {
// 不再能往右了,说明当前节点已是最大的了
if (!node.right) return node;
// 将复杂的问题渐渐减少规模,从而求出小问题的解,最后用小问题的解构建出原问题的答案
return this.maximum(node.right);
}
// 删除最大值
removeMax(node) {
// 临时存储待返回的节点,可是返回以前先对它的平衡进行一下维护。
let returnNode;
const originHeight = node.height; // 记录原节点的高度
// 解决最基本的问题
if (!node.right) {
let leftNode = node.left;
node.left = null;
this.size--;
returnNode = leftNode;
} else {
// 开始化归
node.right = this.removeMax(node.right);
returnNode = node;
}
// 若是本来的节点或者新的节点是空 直接返回空便可 不须要下面的平衡维护
if (!returnNode) return null;
// 在这里对节点的高度进行从新计算 节点自己高度为1
// 计算方式: 1 + 左右子树的height值最大的那个height值
returnNode.height =
1 +
Math.max(
this.getHeight(returnNode.left),
this.getHeight(returnNode.right)
);
// 旧节点的高度若是和新节点的高度一致,就不须要进行节点的平衡维护了
if (originHeight !== returnNode.height) {
// 删除节点后进行节点的平衡维护
// 计算一个节点的平衡因子
const balanceFactor = this.getBalanceFactor(returnNode);
// LL状况 平衡维护 右旋转操做 平衡因子为正数则表示左倾 反之为右倾
if (
balanceFactor > 1 &&
this.getBalanceFactor(returnNode.left) >= 0
)
return this.rightRotate(returnNode);
// RR状况 平衡维护 左旋转操做 平衡因子为负数则表示右倾 反之为左倾
if (
balanceFactor < -1 &&
this.getBalanceFactor(returnNode.right) <= 0
)
return this.leftRotate(returnNode);
// LR状况 平衡维护 先转换为LL状况 再处理LL状况
if (
balanceFactor > 1 &&
this.getBalanceFactor(returnNode.left) < 0
) {
returnNode.left = this.leftRotate(returnNode.left);
return this.rightRotate(returnNode);
}
// RL状况 平衡维护 先转换为RR状况 再处理RR状况
if (
balanceFactor < -1 &&
this.getBalanceFactor(returnNode.right) > 0
) {
returnNode.right = this.rightRotate(returnNode.right);
return this.leftRotate(returnNode);
}
}
return returnNode;
}
// 查询操做 返回查询到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (!node) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (!node) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含该key的元素的判断值 +
contains(key) {
return !!this.getNode(this.root, key);
}
// 返回映射中实际的元素个数 +
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;
}
}
复制代码
MyAVLTreeMap
// 自定义AVLTree映射 AVLTreeMap
class MyAVLTreeMap {
constructor() {
this.myAVLTree = new MyAVLTree();
}
// 添加操做
add(key, value) {
this.MyAVLTree.add(key, value);
}
// 查询操做
get(key) {
return this.MyAVLTree.get(key);
}
// 删除操做
remove(key) {
return this.MyAVLTree.remove(key);
}
// 查看key是否存在
contains(key) {
return this.MyAVLTree.contains(key);
}
// 更新操做
set(key, value) {
this.MyAVLTree.set(key, value);
}
// 获取映射Map中实际元素个数
getSize() {
return this.MyAVLTree.getSize();
}
// 查看映射Map中是否为空
isEmpty() {
return this.MyAVLTree.isEmpty();
}
}
复制代码
MyAVLTreeSet
// 自定义AVLTree集合 AVLTreeSet
class MyAVLTreeSet {
//
constructor() {
this.myAVLTree = new MyAVLTree();
}
add(element) {
this.myAVLTree.add(element, null);
}
remove(element) {
this.myAVLTree.remove(element);
}
contains(element) {
return this.myAVLTree.contains(element);
}
getSize() {
return this.myAVLTree.getSize();
}
isEmpty() {
return this.myAVLTree.isEmpty();
}
}
复制代码
Main
// main 函数
class Main {
constructor() {
this.alterLine('Map Comparison Area');
const n = 2000000;
// const n = 200;
const myBSTMap = new MyBinarySearchTreeMap();
const myAVLTree = new MyAVLTree();
let performanceTest1 = new PerformanceTest();
const random = Math.random;
let arrNumber = new Array(n);
// 循环添加随机数的值
for (let i = 0; i < n; i++) arrNumber[i] = Math.floor(n * random());
this.alterLine('MyBSTMap Comparison Area');
const myBSTMapInfo = performanceTest1.testCustomFn(function() {
// 添加
for (const word of arrNumber)
myBSTMap.add(word, String.fromCharCode(word));
// 删除
for (const word of arrNumber) myBSTMap.remove(word);
// 查找
for (const word of arrNumber)
if (myBSTMap.contains(word))
throw new Error("doesn't remove ok.");
});
// 总毫秒数:
console.log(myBSTMapInfo);
console.log(myBSTMap);
this.show(myBSTMapInfo);
this.alterLine('MyAVLTree Comparison Area');
const that = this;
const myAVLTreeInfo = performanceTest1.testCustomFn(function() {
for (const word of arrNumber)
myAVLTree.add(word, String.fromCharCode(word));
// 输出当前这棵myAVLTree树是不是一个二分搜索树
that.show(
'Is Binary Search Tree : ' + myAVLTree.isBanarySearchTree()
);
console.log(
'Is Binary Search Tree : ' + myAVLTree.isBanarySearchTree()
);
// 输出当前这棵myAVLTree树是不是一个平衡二叉树
that.show('Is Balanced : ' + myAVLTree.isBalanced());
console.log('Is Balanced : ' + myAVLTree.isBalanced());
// 删除
for (const word of arrNumber) {
myAVLTree.remove(word);
}
// // 查找
for (const word of arrNumber)
if (myAVLTree.contains(word))
throw new Error("doesn't remove ok.");
});
console.log(myAVLTree);
// 总毫秒数:
console.log(myAVLTreeInfo);
this.show(myAVLTreeInfo);
}
// 将内容显示在页面上
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();
};
复制代码