【從蛋殼到滿天飛】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
前序遍歷的遞歸寫法面試
前
表示先訪問的這個節點。function preOrder(node) {
if (node == null) return;
// ... 要作的事情
// 訪問該節點
// 先一直往左,而後不斷返回上一層 再向左、終止,
// 最後整個操做循環往復,直到所有終止。
preOrder(node.left);
preOrder(node.right);
}
複製代碼
前序遍歷的非遞歸寫法算法
棧
來模擬遞歸調用時的系統棧。不管是非遞歸仍是遞歸的寫法,結果都是一致的數組
將遞歸算法轉換爲非遞歸算法數據結構
二分搜索樹遍歷的非遞歸實現比遞歸實現複雜不少dom
二分搜索樹的中序遍歷和後序遍歷的非遞歸實現更復雜ide
對於前序遍從來說不管是遞歸寫法仍是非遞歸寫法
(class: MyBinarySearchTree, class: Main)
MyBinarySearchTree
// 自定義二分搜索樹節點
class MyBinarySearchTreeNode {
constructor(element, left = null, right = null) {
// 實際存儲的元素
this.element = element;
// 當前節點的左子樹
this.left = left;
// 當前節點的右子樹
this.right = right;
}
}
// 自定義二分搜索樹
class MyBinarySearchTree {
constructor() {
this.root = null;
this.size = 0;
}
// 添加元素到二分搜索樹中 +
add(element) {
if (element === null) throw new Error("element is null. can't store.");
this.root = this.recursiveAdd(this.root, element);
}
// 添加元素到二分搜索樹中 遞歸算法 -
recursiveAdd(node, newElement) {
// 解決最基本的問題 也就是遞歸函數調用的終止條件
if (node === null) {
this.size++;
return new MyBinarySearchTreeNode(newElement);
}
// 1. 當前節點的元素比新元素大
// 那麼新元素就會被添加到當前節點的左子樹去
// 2. 當前節點的元素比新元素小
// 那麼新元素就會被添加到當前節點的右子樹去
// 3. 當前節點的元素比新元素相等
// 什麼都不作了,由於目前不添加劇復的元素
if (this.compare(node.element, newElement) > 0)
node.left = this.recursiveAdd(node.left, newElement);
else if (this.compare(node.element, newElement) < 0)
node.right = this.recursiveAdd(node.right, newElement);
else {
}
// 將複雜問題分解成多個性質相同的小問題,
// 而後求出小問題的答案,
// 最終構建出原問題的答案
return node;
}
// 判斷二分搜索樹中是否包含某個元素 +
contains(element) {
if (this.root === null) throw new Error("root is null. can't query.");
return this.recursiveContains(this.root, element);
}
// 判斷二分搜索樹種是否包含某個元素 遞歸算法 -
recursiveContains(node, element) {
if (node === null) return false;
// 當前節點元素比 要搜索的元素 大
if (this.compare(node.element, element) > 0)
return this.recursiveContains(node.left, element);
else if (this.compare(node.element, element) < 0)
// 當前元素比 要搜索的元素 小
return this.recursiveContains(node.right, element);
// 兩個元素相等
else return true;
}
// 前序遍歷 +
preOrder(operator) {
this.recursivePreOrder(this.root, operator);
}
// 前序遍歷 遞歸算法 -
recursivePreOrder(node, operator) {
if (node === null) return;
// 調用一下操做方法
operator(node.element);
console.log(node, node.element);
// 繼續遞歸遍歷左右子樹
this.recursivePreOrder(node.left, operator);
this.recursivePreOrder(node.right, operator);
}
// 前序遍歷 非遞歸算法 +
nonRecursivePreOrder(operator) {
let stack = new MyLinkedListStack();
stack.push(this.root);
let node = null;
while (!stack.isEmpty()) {
// 出棧操做
node = stack.pop();
operator(node.element); // 訪問當前的節點
console.log(node.element);
// 棧是先入後出的,把須要後訪問的節點 先放進去,先訪問的節點後放進去
// 前序遍歷是訪問當前節點,而後再遍歷左子樹,最後遍歷右子樹
if (node.right !== null) stack.push(node.right);
if (node.left !== null) stack.push(node.left);
}
}
// 中序遍歷 +
inOrder(operator) {
this.recursiveInOrder(this.root, operator);
}
// 中序遍歷 遞歸算法 -
recursiveInOrder(node, operator) {
if (node == null) return;
this.recursiveInOrder(node.left, operator);
operator(node.element);
console.log(node.element);
this.recursiveInOrder(node.right, operator);
}
// 後序遍歷 +
postOrder(operator) {
this.recursivePostOrder(this.root, operator);
}
// 後序遍歷 遞歸算法 -
recursivePostOrder(node, operator) {
if (node == null) return;
this.recursivePostOrder(node.left, operator);
this.recursivePostOrder(node.right, operator);
operator(node.element);
console.log(node.element);
}
// 獲取二分搜索樹中節點個數 +
getSize() {
return this.size;
}
// 返回二分搜索樹是否爲空的bool值 +
isEmpty() {
return this.size === 0;
}
// 新增一個比較的方法,專門用來比較新增的元素大小 -
// 第一個元素比第二個元素大 就返回 1
// 第一個元素比第二個元素小 就返回 -1
// 第一個元素比第二個元素相等 就返回 0
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is null. can't compare.");
// 先直接寫死
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 輸出二分搜索樹中的信息
// @Override toString 2018-11-03-jwl
toString() {
let treeInfo = '';
treeInfo += this.getBinarySearchTreeString(this.root, 0, treeInfo);
return treeInfo;
}
// 寫一個輔助函數,用來生成二分搜索樹信息的字符串
getBinarySearchTreeString(node, depth, treeInfo, pageContent = '') {
//之前序遍歷的方式
if (node === null) {
treeInfo += this.getDepthString(depth) + 'null \r\n';
pageContent = this.getDepthString(depth) + 'null<br /><br />';
document.body.innerHTML += `${pageContent}`;
return treeInfo;
}
treeInfo += this.getDepthString(depth) + node.element + '\r\n';
pageContent =
this.getDepthString(depth) + node.element + '<br /><br />';
document.body.innerHTML += `${pageContent}`;
treeInfo = this.getBinarySearchTreeString(
node.left,
depth + 1,
treeInfo
);
treeInfo = this.getBinarySearchTreeString(
node.right,
depth + 1,
treeInfo
);
return treeInfo;
}
// 寫一個輔助函數,用來生成遞歸深度字符串
getDepthString(depth) {
let depthString = '';
for (var i = 0; i < depth; i++) {
depthString += '-- ';
}
return depthString;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('MyBinarySearchTree Area');
let myBinarySearchTree = new MyBinarySearchTree();
let nums = [5, 3, 6, 8, 4, 2];
for (var i = 0; i < nums.length; i++) {
myBinarySearchTree.add(nums[i]);
}
/////////////////
// 5 //
// / \ //
// 3 6 //
// / \ \ //
// 2 4 8 //
/////////////////
this.alterLine('MyBinarySearchTree PreOrder Area');
myBinarySearchTree.preOrder(this.show);
this.alterLine('MyBinarySearchTree NonRecursivePreOrder Area');
myBinarySearchTree.nonRecursivePreOrder(this.show);
this.alterLine('MyBinarySearchTree InOrder Area');
myBinarySearchTree.inOrder(this.show);
this.alterLine('MyBinarySearchTree PostOrder Area');
myBinarySearchTree.postOrder(this.show);
}
// 將內容顯示在頁面上
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();
};
複製代碼
(class: MyBinarySearchTree, class: Main)
MyBinarySearchTree
// 自定義二分搜索樹節點
class MyBinarySearchTreeNode {
constructor(element, left = null, right = null) {
// 實際存儲的元素
this.element = element;
// 當前節點的左子樹
this.left = left;
// 當前節點的右子樹
this.right = right;
}
}
// 自定義二分搜索樹
class MyBinarySearchTree {
constructor() {
this.root = null;
this.size = 0;
}
// 添加元素到二分搜索樹中 +
add(element) {
if (element === null) throw new Error("element is null. can't store.");
this.root = this.recursiveAdd(this.root, element);
}
// 添加元素到二分搜索樹中 遞歸算法 -
recursiveAdd(node, newElement) {
// 解決最基本的問題 也就是遞歸函數調用的終止條件
if (node === null) {
this.size++;
return new MyBinarySearchTreeNode(newElement);
}
// 1. 當前節點的元素比新元素大
// 那麼新元素就會被添加到當前節點的左子樹去
// 2. 當前節點的元素比新元素小
// 那麼新元素就會被添加到當前節點的右子樹去
// 3. 當前節點的元素比新元素相等
// 什麼都不作了,由於目前不添加劇復的元素
if (this.compare(node.element, newElement) > 0)
node.left = this.recursiveAdd(node.left, newElement);
else if (this.compare(node.element, newElement) < 0)
node.right = this.recursiveAdd(node.right, newElement);
else {
}
// 將複雜問題分解成多個性質相同的小問題,
// 而後求出小問題的答案,
// 最終構建出原問題的答案
return node;
}
// 判斷二分搜索樹中是否包含某個元素 +
contains(element) {
if (this.root === null) throw new Error("root is null. can't query.");
return this.recursiveContains(this.root, element);
}
// 判斷二分搜索樹種是否包含某個元素 遞歸算法 -
recursiveContains(node, element) {
if (node === null) return false;
// 當前節點元素比 要搜索的元素 大
if (this.compare(node.element, element) > 0)
return this.recursiveContains(node.left, element);
else if (this.compare(node.element, element) < 0)
// 當前元素比 要搜索的元素 小
return this.recursiveContains(node.right, element);
// 兩個元素相等
else return true;
}
// 前序遍歷 +
preOrder(operator) {
this.recursivePreOrder(this.root, operator);
}
// 前序遍歷 遞歸算法 -
recursivePreOrder(node, operator) {
if (node === null) return;
// 調用一下操做方法
operator(node.element);
console.log(node, node.element);
// 繼續遞歸遍歷左右子樹
this.recursivePreOrder(node.left, operator);
this.recursivePreOrder(node.right, operator);
}
// 前序遍歷 非遞歸算法 +
nonRecursivePreOrder(operator) {
let stack = new MyLinkedListStack();
stack.push(this.root);
let node = null;
while (!stack.isEmpty()) {
// 出棧操做
node = stack.pop();
operator(node.element); // 訪問當前的節點
console.log(node.element);
// 棧是先入後出的,把須要後訪問的節點 先放進去,先訪問的節點後放進去
// 前序遍歷是訪問當前節點,而後再遍歷左子樹,最後遍歷右子樹
if (node.right !== null) stack.push(node.right);
if (node.left !== null) stack.push(node.left);
}
}
// 中序遍歷 +
inOrder(operator) {
this.recursiveInOrder(this.root, operator);
}
// 中序遍歷 遞歸算法 -
recursiveInOrder(node, operator) {
if (node == null) return;
this.recursiveInOrder(node.left, operator);
operator(node.element);
console.log(node.element);
this.recursiveInOrder(node.right, operator);
}
// 後序遍歷 +
postOrder(operator) {
this.recursivePostOrder(this.root, operator);
}
// 後序遍歷 遞歸算法 -
recursivePostOrder(node, operator) {
if (node == null) return;
this.recursivePostOrder(node.left, operator);
this.recursivePostOrder(node.right, operator);
operator(node.element);
console.log(node.element);
}
// 層序遍歷
levelOrder(operator) {
let queue = new MyLinkedListQueue();
queue.enqueue(this.root);
let node = null;
while (!queue.isEmpty()) {
node = queue.dequeue();
operator(node.element);
console.log(node.element);
// 隊列 是先進先出的,因此從左往右入隊
// 棧 是後進先出的, 因此從右往左入棧
if (node.left !== null) queue.enqueue(node.left);
if (node.right !== null) queue.enqueue(node.right);
}
}
// 獲取二分搜索樹中節點個數 +
getSize() {
return this.size;
}
// 返回二分搜索樹是否爲空的bool值 +
isEmpty() {
return this.size === 0;
}
// 新增一個比較的方法,專門用來比較新增的元素大小 -
// 第一個元素比第二個元素大 就返回 1
// 第一個元素比第二個元素小 就返回 -1
// 第一個元素比第二個元素相等 就返回 0
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is null. can't compare.");
// 先直接寫死
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 輸出二分搜索樹中的信息
// @Override toString 2018-11-03-jwl
toString() {
let treeInfo = '';
treeInfo += this.getBinarySearchTreeString(this.root, 0, treeInfo);
return treeInfo;
}
// 寫一個輔助函數,用來生成二分搜索樹信息的字符串
getBinarySearchTreeString(node, depth, treeInfo, pageContent = '') {
//之前序遍歷的方式
if (node === null) {
treeInfo += this.getDepthString(depth) + 'null \r\n';
pageContent = this.getDepthString(depth) + 'null<br /><br />';
document.body.innerHTML += `${pageContent}`;
return treeInfo;
}
treeInfo += this.getDepthString(depth) + node.element + '\r\n';
pageContent =
this.getDepthString(depth) + node.element + '<br /><br />';
document.body.innerHTML += `${pageContent}`;
treeInfo = this.getBinarySearchTreeString(
node.left,
depth + 1,
treeInfo
);
treeInfo = this.getBinarySearchTreeString(
node.right,
depth + 1,
treeInfo
);
return treeInfo;
}
// 寫一個輔助函數,用來生成遞歸深度字符串
getDepthString(depth) {
let depthString = '';
for (var i = 0; i < depth; i++) {
depthString += '-- ';
}
return depthString;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('MyBinarySearchTree Area');
let myBinarySearchTree = new MyBinarySearchTree();
let nums = [5, 3, 6, 8, 4, 2];
for (var i = 0; i < nums.length; i++) {
myBinarySearchTree.add(nums[i]);
}
/////////////////
// 5 //
// / \ //
// 3 6 //
// / \ \ //
// 2 4 8 //
/////////////////
this.alterLine('MyBinarySearchTree PreOrder Area');
myBinarySearchTree.preOrder(this.show);
this.alterLine('MyBinarySearchTree NonRecursivePreOrder Area');
myBinarySearchTree.nonRecursivePreOrder(this.show);
this.alterLine('MyBinarySearchTree InOrder Area');
myBinarySearchTree.inOrder(this.show);
this.alterLine('MyBinarySearchTree PostOrder Area');
myBinarySearchTree.postOrder(this.show);
this.alterLine('MyBinarySearchTree LevelOrder Area');
myBinarySearchTree.levelOrder(this.show);
}
// 將內容顯示在頁面上
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();
};
複製代碼
(class: MyBinarySearchTree, class: Main)
MyBinarySearchTree
// 自定義二分搜索樹節點
class MyBinarySearchTreeNode {
constructor(element, left = null, right = null) {
// 實際存儲的元素
this.element = element;
// 當前節點的左子樹
this.left = left;
// 當前節點的右子樹
this.right = right;
}
}
// 自定義二分搜索樹
class MyBinarySearchTree {
constructor() {
this.root = null;
this.size = 0;
}
// 添加元素到二分搜索樹中 +
add(element) {
if (element === null) throw new Error("element is null. can't store.");
this.root = this.recursiveAdd(this.root, element);
}
// 添加元素到二分搜索樹中 遞歸算法 -
recursiveAdd(node, newElement) {
// 解決最基本的問題 也就是遞歸函數調用的終止條件
if (node === null) {
this.size++;
return new MyBinarySearchTreeNode(newElement);
}
// 1. 當前節點的元素比新元素大
// 那麼新元素就會被添加到當前節點的左子樹去
// 2. 當前節點的元素比新元素小
// 那麼新元素就會被添加到當前節點的右子樹去
// 3. 當前節點的元素比新元素相等
// 什麼都不作了,由於目前不添加劇復的元素
if (this.compare(node.element, newElement) > 0)
node.left = this.recursiveAdd(node.left, newElement);
else if (this.compare(node.element, newElement) < 0)
node.right = this.recursiveAdd(node.right, newElement);
else {
}
// 將複雜問題分解成多個性質相同的小問題,
// 而後求出小問題的答案,
// 最終構建出原問題的答案
return node;
}
// 判斷二分搜索樹中是否包含某個元素 +
contains(element) {
if (this.root === null) throw new Error("root is null. can't query.");
return this.recursiveContains(this.root, element);
}
// 判斷二分搜索樹種是否包含某個元素 遞歸算法 -
recursiveContains(node, element) {
if (node === null) return false;
// 當前節點元素比 要搜索的元素 大
if (this.compare(node.element, element) > 0)
return this.recursiveContains(node.left, element);
else if (this.compare(node.element, element) < 0)
// 當前元素比 要搜索的元素 小
return this.recursiveContains(node.right, element);
// 兩個元素相等
else return true;
}
// 找到二分搜索樹中的最大值的元素 +
maximum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMaximum(this.root).element;
}
// 找到二分搜索樹中的最大值的元素的節點 遞歸算法 -
recursiveMaximum(node) {
// 解決最基本的問題 向右走再也走不動了,說明當前節點就是最大值節點。
if (node.right === null) return node;
return this.recursiveMaximum(node.right);
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點的元素 +
removeMax() {
let maxElement = this.maximum();
this.root = this.recursiveRemoveMax(this.root);
return maxElement;
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMax(node) {
if (node.right === null) {
// 先存 當前這個節點的左子樹,
// 由於可能當前這個節點僅僅沒有右子樹,只有左子樹,
// 那麼左子樹能夠替代當前這個節點。
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
node.right = this.recursiveRemoveMax(node.right);
return node;
}
// 找到二分搜索樹中的最小值 +
minimum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMinimum(this.root).element;
}
// 找到二分搜索樹中的最小值的元素的節點 遞歸算法 -
recursiveMinimum(node) {
if (node.left === null) return node;
return this.recursiveMinimum(node.left);
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點的元素 +
removeMin() {
let leftNode = this.minimum();
this.root = this.recursiveRemoveMin(this.root);
return leftNode;
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMin(node) {
// 解決最簡單的問題
if (node.left == null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 將複雜的問題拆分爲性質相同的小問題,
// 而後求出這些小問題的解後構建出原問題的答案
node.left = this.recursiveRemoveMin(node.left);
return node;
}
// 前序遍歷 +
preOrder(operator) {
this.recursivePreOrder(this.root, operator);
}
// 前序遍歷 遞歸算法 -
recursivePreOrder(node, operator) {
if (node === null) return;
// 調用一下操做方法
operator(node.element);
console.log(node, node.element);
// 繼續遞歸遍歷左右子樹
this.recursivePreOrder(node.left, operator);
this.recursivePreOrder(node.right, operator);
}
// 前序遍歷 非遞歸算法 +
nonRecursivePreOrder(operator) {
let stack = new MyLinkedListStack();
stack.push(this.root);
let node = null;
while (!stack.isEmpty()) {
// 出棧操做
node = stack.pop();
operator(node.element); // 訪問當前的節點
console.log(node.element);
// 棧是先入後出的,把須要後訪問的節點 先放進去,先訪問的節點後放進去
// 前序遍歷是訪問當前節點,而後再遍歷左子樹,最後遍歷右子樹
if (node.right !== null) stack.push(node.right);
if (node.left !== null) stack.push(node.left);
}
}
// 中序遍歷 +
inOrder(operator) {
this.recursiveInOrder(this.root, operator);
}
// 中序遍歷 遞歸算法 -
recursiveInOrder(node, operator) {
if (node == null) return;
this.recursiveInOrder(node.left, operator);
operator(node.element);
console.log(node.element);
this.recursiveInOrder(node.right, operator);
}
// 後序遍歷 +
postOrder(operator) {
this.recursivePostOrder(this.root, operator);
}
// 後序遍歷 遞歸算法 -
recursivePostOrder(node, operator) {
if (node == null) return;
this.recursivePostOrder(node.left, operator);
this.recursivePostOrder(node.right, operator);
operator(node.element);
console.log(node.element);
}
// 層序遍歷
levelOrder(operator) {
let queue = new MyLinkedListQueue();
queue.enqueue(this.root);
let node = null;
while (!queue.isEmpty()) {
node = queue.dequeue();
operator(node.element);
console.log(node.element);
// 隊列 是先進先出的,因此從左往右入隊
// 棧 是後進先出的, 因此從右往左入棧
if (node.left !== null) queue.enqueue(node.left);
if (node.right !== null) queue.enqueue(node.right);
}
}
// 獲取二分搜索樹中節點個數 +
getSize() {
return this.size;
}
// 返回二分搜索樹是否爲空的bool值 +
isEmpty() {
return this.size === 0;
}
// 新增一個比較的方法,專門用來比較新增的元素大小 -
// 第一個元素比第二個元素大 就返回 1
// 第一個元素比第二個元素小 就返回 -1
// 第一個元素比第二個元素相等 就返回 0
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is null. can't compare.");
// 先直接寫死
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 輸出二分搜索樹中的信息
// @Override toString 2018-11-03-jwl
toString() {
let treeInfo = '';
treeInfo += this.getBinarySearchTreeString(this.root, 0, treeInfo);
return treeInfo;
}
// 寫一個輔助函數,用來生成二分搜索樹信息的字符串
getBinarySearchTreeString(node, depth, treeInfo, pageContent = '') {
//之前序遍歷的方式
if (node === null) {
treeInfo += this.getDepthString(depth) + 'null \r\n';
pageContent = this.getDepthString(depth) + 'null<br /><br />';
document.body.innerHTML += `${pageContent}`;
return treeInfo;
}
treeInfo += this.getDepthString(depth) + node.element + '\r\n';
pageContent =
this.getDepthString(depth) + node.element + '<br /><br />';
document.body.innerHTML += `${pageContent}`;
treeInfo = this.getBinarySearchTreeString(
node.left,
depth + 1,
treeInfo
);
treeInfo = this.getBinarySearchTreeString(
node.right,
depth + 1,
treeInfo
);
return treeInfo;
}
// 寫一個輔助函數,用來生成遞歸深度字符串
getDepthString(depth) {
let depthString = '';
for (var i = 0; i < depth; i++) {
depthString += '-- ';
}
return depthString;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('MyBinarySearchTree remove Min Node Area');
{
let tree = new MyBinarySearchTree();
let n = 100;
let random = Math.random;
for (var i = 0; i < n; i++) {
tree.add(n * n * n * random());
}
let array = new MyArray(n);
while (!tree.isEmpty()) {
array.add(tree.removeMin());
}
// 數組中的元素從小到大排序的
console.log(array.toString());
for (var i = 1; i < n; i++) {
//若是數組後面的元素小於數組前面的元素
if (array.get(i) < array.get(i - 1))
throw new Error(
'error. array element is not (small - big) sort.'
);
}
console.log('removeMin test completed.');
this.show('removeMin test completed.');
}
this.alterLine('MyBinarySearchTree remove Max Node Area');
{
let tree = new MyBinarySearchTree();
let n = 100;
let random = Math.random;
for (var i = 0; i < n; i++) {
tree.add(n * n * n * random());
}
let array = new MyArray(n);
while (!tree.isEmpty()) {
array.add(tree.removeMax());
}
// 數組中的元素從大到小排序的
console.log(array.toString());
for (var i = 1; i < n; i++) {
//若是數組後面的元素大於數組前面的元素
if (array.get(i) > array.get(i - 1))
throw new Error(
'error. array element is not (big - small) sort.'
);
}
console.log('removeMax test completed.');
this.show('removeMax test completed.');
}
}
// 將內容顯示在頁面上
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();
};
複製代碼
(class: MyBinarySearchTree, class: Main)
MyBinarySearchTree
// 自定義二分搜索樹節點
class MyBinarySearchTreeNode {
constructor(element, left = null, right = null) {
// 實際存儲的元素
this.element = element;
// 當前節點的左子樹
this.left = left;
// 當前節點的右子樹
this.right = right;
}
}
// 自定義二分搜索樹
class MyBinarySearchTree {
constructor() {
this.root = null;
this.size = 0;
}
// 添加元素到二分搜索樹中 +
add(element) {
if (element === null) throw new Error("element is null. can't store.");
this.root = this.recursiveAdd(this.root, element);
}
// 添加元素到二分搜索樹中 遞歸算法 -
recursiveAdd(node, newElement) {
// 解決最基本的問題 也就是遞歸函數調用的終止條件
if (node === null) {
this.size++;
return new MyBinarySearchTreeNode(newElement);
}
// 1. 當前節點的元素比新元素大
// 那麼新元素就會被添加到當前節點的左子樹去
// 2. 當前節點的元素比新元素小
// 那麼新元素就會被添加到當前節點的右子樹去
// 3. 當前節點的元素比新元素相等
// 什麼都不作了,由於目前不添加劇復的元素
if (this.compare(node.element, newElement) > 0)
node.left = this.recursiveAdd(node.left, newElement);
else if (this.compare(node.element, newElement) < 0)
node.right = this.recursiveAdd(node.right, newElement);
else {
}
// 將複雜問題分解成多個性質相同的小問題,
// 而後求出小問題的答案,
// 最終構建出原問題的答案
return node;
}
// 判斷二分搜索樹中是否包含某個元素 +
contains(element) {
if (this.root === null) throw new Error("root is null. can't query.");
return this.recursiveContains(this.root, element);
}
// 判斷二分搜索樹種是否包含某個元素 遞歸算法 -
recursiveContains(node, element) {
if (node === null) return false;
// 當前節點元素比 要搜索的元素 大
if (this.compare(node.element, element) > 0)
return this.recursiveContains(node.left, element);
else if (this.compare(node.element, element) < 0)
// 當前元素比 要搜索的元素 小
return this.recursiveContains(node.right, element);
// 兩個元素相等
else return true;
}
// 找到二分搜索樹中的最大值的元素 +
maximum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMaximum(this.root).element;
}
// 找到二分搜索樹中的最大值的元素的節點 遞歸算法 -
recursiveMaximum(node) {
// 解決最基本的問題 向右走再也走不動了,說明當前節點就是最大值節點。
if (node.right === null) return node;
return this.recursiveMaximum(node.right);
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點的元素 +
removeMax() {
let maxElement = this.maximum();
this.root = this.recursiveRemoveMax(this.root);
return maxElement;
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMax(node) {
if (node.right === null) {
// 先存 當前這個節點的左子樹,
// 由於可能當前這個節點僅僅沒有右子樹,只有左子樹,
// 那麼左子樹能夠替代當前這個節點。
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
node.right = this.recursiveRemoveMax(node.right);
return node;
}
// 找到二分搜索樹中的最小值 +
minimum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMinimum(this.root).element;
}
// 找到二分搜索樹中的最小值的元素的節點 遞歸算法 -
recursiveMinimum(node) {
if (node.left === null) return node;
return this.recursiveMinimum(node.left);
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點的元素 +
removeMin() {
let leftNode = this.minimum();
this.root = this.recursiveRemoveMin(this.root);
return leftNode;
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMin(node) {
// 解決最簡單的問題
if (node.left == null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 將複雜的問題拆分爲性質相同的小問題,
// 而後求出這些小問題的解後構建出原問題的答案
node.left = this.recursiveRemoveMin(node.left);
return node;
}
// 刪除二分搜索樹上的任意節點
remove(element) {
this.root = this.recursiveRemove(this.root, element);
}
// 刪除二分搜索樹上的任意節點 遞歸算法
// 返回刪除對應元素節點後新的二分搜索樹的根
recursiveRemove(node, element) {
if (node === null) return null;
// 當前節點的元素值比待刪除的元素小 那麼就向當前節點的右子樹中去找
if (this.compare(node.element, element) < 0) {
node.right = this.recursiveRemove(node.right, element);
return node;
} else if (this.compare(node.element, element) > 0) {
// 向當前節點的左子樹中去找
node.left = this.recursiveRemove(node.left, element);
return node;
} else {
// 若是找到了相同值的節點了,開始進行相應的處理
// 若是這個節點左子樹爲空,那麼就讓這個節點的右子樹覆蓋當前節點
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 若是當前節點的右子樹爲空,那麼就讓這個節點的左子樹覆蓋當前節點
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 若是當前節點的左右子樹都不爲空,那麼就開始特殊操做
// 1. 先找到當前節點右子樹上最小的那個節點,保存起來
// 2. 而後刪除掉當前節點右子樹上最小的那個節點,
// 3. 讓保存起來的那個節點覆蓋掉當前節點
// 1. 也就是保存起來的那個節點的right = 刪除掉當前節點右子樹上最小的節點後返回的那個節點
// 2. 再讓保存起來的那個節點的left = 當前節點的left
// 4. 解除當前節點及其left和right,全都賦值爲null,這樣就至關於把當前節點從二分搜索樹中剔除了
// 5. 返回保存的這個節點
let successtor = this.recursiveMinimum(node.right);
successtor.right = this.recursiveRemoveMin(node.right);
// 恢復removeMin 操做的this.size -- 帶來的影響
this.size++;
successtor.left = node.left;
// 開始正式的刪除當前節點的操做
node = node.left = node.right = null;
this.size--;
// 返回當前保存的節點
return successtor;
}
}
// 前序遍歷 +
preOrder(operator) {
this.recursivePreOrder(this.root, operator);
}
// 前序遍歷 遞歸算法 -
recursivePreOrder(node, operator) {
if (node === null) return;
// 調用一下操做方法
operator(node.element);
console.log(node, node.element);
// 繼續遞歸遍歷左右子樹
this.recursivePreOrder(node.left, operator);
this.recursivePreOrder(node.right, operator);
}
// 前序遍歷 非遞歸算法 +
nonRecursivePreOrder(operator) {
let stack = new MyLinkedListStack();
stack.push(this.root);
let node = null;
while (!stack.isEmpty()) {
// 出棧操做
node = stack.pop();
operator(node.element); // 訪問當前的節點
console.log(node.element);
// 棧是先入後出的,把須要後訪問的節點 先放進去,先訪問的節點後放進去
// 前序遍歷是訪問當前節點,而後再遍歷左子樹,最後遍歷右子樹
if (node.right !== null) stack.push(node.right);
if (node.left !== null) stack.push(node.left);
}
}
// 中序遍歷 +
inOrder(operator) {
this.recursiveInOrder(this.root, operator);
}
// 中序遍歷 遞歸算法 -
recursiveInOrder(node, operator) {
if (node == null) return;
this.recursiveInOrder(node.left, operator);
operator(node.element);
console.log(node.element);
this.recursiveInOrder(node.right, operator);
}
// 後序遍歷 +
postOrder(operator) {
this.recursivePostOrder(this.root, operator);
}
// 後序遍歷 遞歸算法 -
recursivePostOrder(node, operator) {
if (node == null) return;
this.recursivePostOrder(node.left, operator);
this.recursivePostOrder(node.right, operator);
operator(node.element);
console.log(node.element);
}
// 層序遍歷
levelOrder(operator) {
let queue = new MyLinkedListQueue();
queue.enqueue(this.root);
let node = null;
while (!queue.isEmpty()) {
node = queue.dequeue();
operator(node.element);
console.log(node.element);
// 隊列 是先進先出的,因此從左往右入隊
// 棧 是後進先出的, 因此從右往左入棧
if (node.left !== null) queue.enqueue(node.left);
if (node.right !== null) queue.enqueue(node.right);
}
}
// 獲取二分搜索樹中節點個數 +
getSize() {
return this.size;
}
// 返回二分搜索樹是否爲空的bool值 +
isEmpty() {
return this.size === 0;
}
// 新增一個比較的方法,專門用來比較新增的元素大小 -
// 第一個元素比第二個元素大 就返回 1
// 第一個元素比第二個元素小 就返回 -1
// 第一個元素比第二個元素相等 就返回 0
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is null. can't compare.");
// 先直接寫死
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 輸出二分搜索樹中的信息
// @Override toString 2018-11-03-jwl
toString() {
let treeInfo = '';
treeInfo += this.getBinarySearchTreeString(this.root, 0, treeInfo);
return treeInfo;
}
// 寫一個輔助函數,用來生成二分搜索樹信息的字符串
getBinarySearchTreeString(node, depth, treeInfo, pageContent = '') {
//之前序遍歷的方式
if (node === null) {
treeInfo += this.getDepthString(depth) + 'null \r\n';
pageContent = this.getDepthString(depth) + 'null<br /><br />';
document.body.innerHTML += `${pageContent}`;
return treeInfo;
}
treeInfo += this.getDepthString(depth) + node.element + '\r\n';
pageContent =
this.getDepthString(depth) + node.element + '<br /><br />';
document.body.innerHTML += `${pageContent}`;
treeInfo = this.getBinarySearchTreeString(
node.left,
depth + 1,
treeInfo
);
treeInfo = this.getBinarySearchTreeString(
node.right,
depth + 1,
treeInfo
);
return treeInfo;
}
// 寫一個輔助函數,用來生成遞歸深度字符串
getDepthString(depth) {
let depthString = '';
for (var i = 0; i < depth; i++) {
depthString += '-- ';
}
return depthString;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('MyBinarySearchTree Remove Node Area');
{
let n = 100;
let tree = new MyBinarySearchTree();
let array = new MyArray(n);
let random = Math.random;
for (var i = 0; i < n; i++) {
tree.add(n * n * n * random());
array.add(tree.removeMin());
}
// 數組中的元素從小到大排序的
console.log(array.toString());
for (var i = 0; i < n; i++) {
tree.remove(array.get(i));
}
console.log(
'removeMin test ' +
(tree.isEmpty() ? 'completed.' : 'no completed.')
);
this.show(
'removeMin test ' +
(tree.isEmpty() ? 'completed.' : 'no completed.')
);
}
}
// 將內容顯示在頁面上
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();
};
複製代碼
root.size
就行了,很是的方便。count++
便可,count--
便可,https://leetcode-cn.com/tag/tree/