【從蛋殼到滿天飛】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();
};
複製代碼