【從蛋殼到滿天飛】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 (一個一個的工程)java
所有源代碼已上傳 github,點擊我吧,光看文章可以掌握兩成,動手敲代碼、動腦思考、畫圖才能夠掌握八成。node
本文章適合 對數據結構想了解而且感興趣的人羣,文章風格一如既往如此,就以爲手機上看起來比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時間跨度也算將近半年時間了,但願對想學習數據結構的人或者正在學習數據結構的人羣有幫助。python
O(logn)
這個級別,O(logn)
這個級別已經很是高效了,可是若是字典中有 100 萬個條目,O(w)
這個級別,class TrieNode {
c; // char
next; // Array(26)
}
複製代碼
@:/\_-
等等,class TrieNode {
c; // char
next; // Map
}
複製代碼
char c
是沒有問題的,在 trie 中添加或者查詢某一個單詞的時候,char c
是沒有問題的,char c
,class TrieNode {
next; // Map
}
複製代碼
boolean isWord
,class TrieNode {
isWord; // Boolean
next; // Map
}
複製代碼
MyTriec++
// 自定義字典樹節點 TrieNode
class MyTrieNode {
constructor(letterChar, isWord = false) {
this.letterChar = letterChar;
this.isWord = isWord; // 是不是單詞
this.next = new Map(); // 存儲 字符所對應的節點的 字典映射
}
}
// 自定義字典樹 Trie
class MyTrie {
constructor() {
this.root = new MyTrieNode();
this.size = 0;
}
// 向Trie中添加一個新的單詞word
add(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNode(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
this.size++;
}
}
// 向Trie中添加一個新的單詞word 遞歸算法
recursiveAdd(word) {
this.recursiveAddFn(this.root, word, 0);
}
// 向Trie中添加一個新的單詞word 遞歸輔助函數
recursiveAddFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) {
if (!node.isWord) {
node.isWord = true;
this.size++;
}
return;
}
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空就添加
if (!map.has(letterChar))
map.set(letterChar, new MyTrieNode(letterChar));
recursiveAddFn(map.get(letterChar), word, index + 1);
}
}
複製代碼
存儲字符串
這樣的元素的相應的集合
,改形成
了一個映射
了。(class: MyTrie, class: MyTrieSet, class: Main)
git
MyTriegithub
// 自定義字典樹節點 TrieNode
class MyTrieNode {
constructor(letterChar, isWord = false) {
this.letterChar = letterChar;
this.isWord = isWord; // 是不是單詞
this.next = new Map(); // 存儲 字符所對應的節點的 字典映射
}
}
// 自定義字典樹 Trie
class MyTrie {
constructor() {
this.root = new MyTrieNode();
this.size = 0;
}
// 向Trie中添加一個新的單詞word
add(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNode(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
this.size++;
}
}
// 向Trie中添加一個新的單詞word 遞歸算法
recursiveAdd(word) {
this.recursiveAddFn(this.root, word, 0);
}
// 向Trie中添加一個新的單詞word 遞歸輔助函數 -
recursiveAddFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) {
if (!node.isWord) {
node.isWord = true;
this.size++;
}
return;
}
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空就添加
if (!map.has(letterChar))
map.set(letterChar, new MyTrieNode(letterChar));
this.recursiveAddFn(map.get(letterChar), word, index + 1);
}
// 查詢單詞word是否在Trie中
contains(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (node === null) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
// 返回最後一個字符是不是一個單詞的結尾
return cur.isWord;
}
// 查詢單詞word是否在Trie中 遞歸算法
recursiveContains(word) {
return this.recursiveContainsFn(this.root, word, 0);
}
// 查詢單詞word是否在Trie中 遞歸賦值函數 -
recursiveContainsFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空那麼就說明這個單詞沒有進行存儲
if (!map.has(letterChar)) return false;
return this.recursiveContainsFn(map.get(letterChar), word, index + 1);
}
// 獲取字典樹中存儲的單詞數量
getSize() {
return this.size;
}
// 獲取字典樹中是否爲空
isEmpty() {
return this.size === 0;
}
}
複製代碼
MyTrieSet正則表達式
// 自定義字典集合 TrieSet
class MyTrieSet {
constructor() {
this.trie = new MyTrie();
}
// 添加操做
add(word) {
this.trie.add(word);
}
// 刪除操做 待實現
remove(word) {
return false;
}
// 查單詞是否存在
contains(word) {
return this.trie.contains(word);
}
// 獲取實際元素個數
getSize() {
return this.trie.getSize();
}
// 獲取當前集合是否爲空
isEmpty() {
return this.trie.isEmpty();
}
}
複製代碼
Main算法
// main 函數
class Main {
constructor() {
this.alterLine('Set Comparison Area');
const n = 2000000;
const myBSTSet = new MyBinarySearchTreeSet();
const myTrieSet = new MyTrieSet();
let performanceTest1 = new PerformanceTest();
const random = Math.random;
let arr = [];
// 循環添加隨機數的值
for (let i = 0; i < n; i++) {
arr.push(i.toString());
}
this.alterLine('MyBSTSet Comparison Area');
const myBSTSetInfo = performanceTest1.testCustomFn(function() {
for (const word of arr) myBSTSet.add(word);
});
// 總毫秒數:3173
console.log(myBSTSetInfo);
this.show(myBSTSetInfo);
this.alterLine('MyTrieSet Comparison Area');
const myTrieSetInfo = performanceTest1.testCustomFn(function() {
for (const word of arr) myTrieSet.add(word);
});
// 總毫秒數:2457
console.log(myTrieSetInfo);
this.show(myTrieSetInfo);
}
// 將內容顯示在頁面上
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();
};
複製代碼
208.實現 Trie (前綴樹)
編程
https://leetcode-cn.com/problems/implement-trie-prefix-tree/
Trie
// 答題
class Solution {
// leetcode 208.實現 Trie (前綴樹)
Trie() {
// 數組版的Trie 靜態Trie
function ArrayTrie() {
// TrieNode
var TrieNode = function(isWord = false) {
this.isWord = isWord;
this.next = new Array(26);
};
/** * Initialize your data structure here. */
var Trie = function() {
this.root = new TrieNode();
};
/** * Inserts a word into the trie. * @param {string} word * @return {void} */
Trie.prototype.insert = function(word) {
// 指定遊標
let cur = this.root;
for (const c of word) {
const index = c.charCodeAt(0) - 97;
const array = cur.next;
if (array[index] === null || array[index] === undefined)
array[index] = new TrieNode();
cur = array[index];
}
if (!cur.isWord) cur.isWord = true;
};
/** * Returns if the word is in the trie. * @param {string} word * @return {boolean} */
Trie.prototype.search = function(word) {
// 指定遊標
let cur = this.root;
for (const c of word) {
const index = c.charCodeAt(0) - 97;
const array = cur.next;
if (array[index] === null || array[index] === undefined)
return false;
cur = array[index];
}
return cur.isWord;
};
/** * Returns if there is any word in the trie that starts with the given prefix. * @param {string} prefix * @return {boolean} */
Trie.prototype.startsWith = function(prefix) {
// 指定遊標
let cur = this.root;
for (const c of prefix) {
const index = c.charCodeAt(0) - 97;
const array = cur.next;
if (array[index] === null || array[index] === undefined)
return false;
cur = array[index];
}
return true;
};
/** * Your Trie object will be instantiated and called as such: * var obj = Object.create(Trie).createNew() * obj.insert(word) * var param_2 = obj.search(word) * var param_3 = obj.startsWith(prefix) */
return new Trie();
}
// 映射版的Trie 動態Trie
function MapTrie() {
// TrieNode
var TrieNode = function(isWord = false) {
this.isWord = isWord;
this.next = new Map();
};
/** * Initialize your data structure here. */
var Trie = function() {
this.root = new TrieNode();
};
/** * Inserts a word into the trie. * @param {string} word * @return {void} */
Trie.prototype.insert = function(word) {
// 指定遊標
let cur = this.root;
for (const c of word) {
const map = cur.next;
if (!map.has(c)) map.set(c, new TrieNode());
cur = map.get(c);
}
if (!cur.isWord) cur.isWord = true;
};
/** * Returns if the word is in the trie. * @param {string} word * @return {boolean} */
Trie.prototype.search = function(word) {
// 指定遊標
let cur = this.root;
for (const c of word) {
const map = cur.next;
if (!map.has(c)) return false;
cur = map.get(c);
}
return cur.isWord;
};
/** * Returns if there is any word in the trie that starts with the given prefix. * @param {string} prefix * @return {boolean} */
Trie.prototype.startsWith = function(prefix) {
// 指定遊標
let cur = this.root;
for (const c of prefix) {
const map = cur.next;
if (!map.has(c)) return false;
cur = map.get(c);
}
return true;
};
/** * Your Trie object will be instantiated and called as such: * var obj = Object.create(Trie).createNew() * obj.insert(word) * var param_2 = obj.search(word) * var param_3 = obj.startsWith(prefix) */
return new Trie();
}
// return new ArrayTrie();
return new MapTrie();
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('leetcode 208.實現 Trie (前綴樹)');
let s = new Solution();
let trie = s.Trie();
this.show(trie.insert('apple') + '');
this.show(trie.search('apple') + ' // 返回 true'); // 返回 true
this.show(trie.search('app') + '// 返回 false'); // 返回 false
this.show(trie.startsWith('app') + '// 返回 true'); // 返回 true
this.show(trie.insert('app') + '');
this.show(trie.search('app') + '// 返回 true'); // 返回 true
}
// 將內容顯示在頁面上
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();
};
複製代碼
// 自定義字典樹 Trie
class MyTrie {
constructor() {
this.root = new MyTrieNode();
this.size = 0;
}
// 向Trie中添加一個新的單詞word
add(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNode(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
this.size++;
}
}
// 向Trie中添加一個新的單詞word 遞歸算法
recursiveAdd(word) {
this.recursiveAddFn(this.root, word, 0);
}
// 向Trie中添加一個新的單詞word 遞歸輔助函數 -
recursiveAddFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) {
if (!node.isWord) {
node.isWord = true;
this.size++;
}
return;
}
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空就添加
if (!map.has(letterChar)) map.set(letterChar, new MyTrieNode(letterChar));
this.recursiveAddFn(map.get(letterChar), word, index + 1);
}
// 查詢單詞word是否在Trie中
contains(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (node === null) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
// 返回最後一個字符是不是一個單詞的結尾
return cur.isWord;
}
// 查詢單詞word是否在Trie中 遞歸算法
recursiveContains(word) {
return this.recursiveContainsFn(this.root, word, 0);
}
// 查詢單詞word是否在Trie中 遞歸賦值函數 -
recursiveContainsFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空那麼就說明這個單詞沒有進行存儲
if (!map.has(letterChar)) return false;
return this.recursiveContainsFn(map.get(letterChar), word, index + 1);
}
// 查詢在Trie中是否有單詞以 prefix 爲前綴
isPrefix(prefix) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of prefix) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (node === null) return false;
// 遊標切換到這個節點
cur = node;
}
// 前綴遍歷完畢 說明這個前綴有單詞與之匹配
return true;
}
// 獲取字典樹中存儲的單詞數量
getSize() {
return this.size;
}
// 獲取字典樹中是否爲空
isEmpty() {
return this.size === 0;
}
}
複製代碼
.
,.
。.
,那麼就很簡單,.
,那麼就相對來講複雜一點,211.添加與搜索單詞 - 數據結構設計
https://leetcode-cn.com/problems/add-and-search-word-data-structure-design/
WordDictionary
// 答題
class Solution {
// leetcode 211.添加與搜索單詞 - 數據結構設計
WordDictionary() {
// 數組版
function ArrayWordDictionary() {
// TrieNode
var TrieNode = function() {
this.isWord = false;
this.next = new Array(26);
};
/** * Initialize your data structure here. */
var WordDictionary = function() {
this.root = new TrieNode();
};
/** * Adds a word into the data structure. * @param {string} word * @return {void} */
WordDictionary.prototype.addWord = function(word) {
// 指定遊標
let cur = this.root;
for (const c of word) {
const index = c.charCodeAt(0) - 97;
const array = cur.next;
if (!array[index]) array[index] = new TrieNode();
cur = array[index];
}
if (!cur.isWord) cur.isWord = true;
};
/** * Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. * @param {string} word * @return {boolean} */
WordDictionary.prototype.search = function(word) {
return this.recursiveMatch(this.root, word, 0);
};
// 遞歸搜索
WordDictionary.prototype.recursiveMatch = function( node, word, index ) {
if (index === word.length) return node.isWord;
const letterChar = word[index];
if (letterChar !== '.') {
const i = letterChar.charCodeAt(0) - 97;
if (!node.next[i]) return false;
return this.recursiveMatch(node.next[i], word, index + 1);
} else {
for (const next of node.next) {
if (next === undefined) continue;
if (this.recursiveMatch(next, word, index + 1))
return true;
}
return false;
}
};
/** * Your WordDictionary object will be instantiated and called as such: * var obj = Object.create(WordDictionary).createNew() * obj.addWord(word) * var param_2 = obj.search(word) */
return new WordDictionary();
}
// 映射版
function MapWordDictionary() {
// TrieNode
var TrieNode = function(isWord = false) {
this.isWord = isWord;
this.next = new Map();
};
/** * Initialize your data structure here. */
var WordDictionary = function() {
this.root = new TrieNode();
};
/** * Adds a word into the data structure. * @param {string} word * @return {void} */
WordDictionary.prototype.addWord = function(word) {
let cur = this.root;
for (const c of word) {
if (!cur.next.has(c)) cur.next.set(c, new TrieNode());
cur = cur.next.get(c);
}
if (!cur.isWord) cur.isWord = true;
};
/** * Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. * @param {string} word * @return {boolean} */
WordDictionary.prototype.search = function(word) {
return this.recursiveMatch(this.root, word, 0);
};
WordDictionary.prototype.recursiveMatch = function( node, word, index ) {
if (index === word.length) return node.isWord;
const letterChar = word[index];
if (letterChar !== '.') {
const map = node.next;
if (!map.has(letterChar)) return false;
return this.recursiveMatch(
map.get(letterChar),
word,
index + 1
);
} else {
const map = node.next;
const keys = map.keys();
for (const key of keys)
if (this.recursiveMatch(map.get(key), word, index + 1))
return true;
return false;
}
};
/** * Your WordDictionary object will be instantiated and called as such: * var obj = Object.create(WordDictionary).createNew() * obj.addWord(word) * var param_2 = obj.search(word) */
return new WordDictionary();
}
// return new ArrayWordDictionary();
return new MapWordDictionary();
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('leetcode 208. 實現 Trie (前綴樹)');
let trie = new MyTrie();
this.show(trie.add('apple') + '');
this.show(trie.contains('apple') + ' // 返回 true'); // 返回 true
this.show(trie.contains('app') + '// 返回 false'); // 返回 false
this.show(trie.isPrefix('app') + '// 返回 true'); // 返回 true
this.show(trie.add('app') + '');
this.show(trie.contains('app') + '// 返回 true'); // 返回 true
this.alterLine('leetcode 211. 添加與搜索單詞 - 數據結構設計');
trie = new MyTrie();
this.show(trie.add('bad') + '');
this.show(trie.add('dad') + '');
this.show(trie.add('mad') + '');
this.show(trie.regexpSearch('pad') + '-> false'); //-> false
this.show(trie.regexpSearch('bad') + '-> true'); //-> true
this.show(trie.regexpSearch('.ad') + '-> true'); //-> true
this.show(trie.regexpSearch('b..') + '-> true'); //-> true
this.show(trie.regexpSearch('b....') + '-> false'); //-> false
}
// 將內容顯示在頁面上
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();
};
複製代碼
MyTrie
// 自定義字典樹 Trie
class MyTrie {
constructor() {
this.root = new MyTrieNode();
this.size = 0;
}
// 向Trie中添加一個新的單詞word
add(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNode(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
this.size++;
}
}
// 向Trie中添加一個新的單詞word 遞歸算法
recursiveAdd(word) {
this.recursiveAddFn(this.root, word, 0);
}
// 向Trie中添加一個新的單詞word 遞歸輔助函數 -
recursiveAddFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) {
if (!node.isWord) {
node.isWord = true;
this.size++;
}
return;
}
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空就添加
if (!map.has(letterChar))
map.set(letterChar, new MyTrieNode(letterChar));
this.recursiveAddFn(map.get(letterChar), word, index + 1);
}
// 查詢單詞word是否在Trie中
contains(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (node === null) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
// 返回最後一個字符是不是一個單詞的結尾
return cur.isWord;
}
// 查詢單詞word是否在Trie中 遞歸算法
recursiveContains(word) {
return this.recursiveContainsFn(this.root, word, 0);
}
// 查詢單詞word是否在Trie中 遞歸賦值函數 -
recursiveContainsFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空那麼就說明這個單詞沒有進行存儲
if (!map.has(letterChar)) return false;
return this.recursiveContainsFn(map.get(letterChar), word, index + 1);
}
// 查詢在Trie中是否有單詞以 prefix 爲前綴
isPrefix(prefix) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of prefix) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (node === null) return false;
// 遊標切換到這個節點
cur = node;
}
// 前綴遍歷完畢 說明這個前綴有單詞與之匹配
return true;
}
// 正則表達式 查詢單詞word是否在Trie中,目前只支持 統配符 "."
regexpSearch(regexpWord) {
return this.match(this.root, regexpWord, 0);
}
// 正則表達式 匹配單詞 遞歸算法 -
match(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 判斷這個字符是不是通配符
if (letterChar !== '.') {
// 若是映射中不包含這個字符
if (!map.has(letterChar)) return false;
// 若是映射中包含這個字符,那麼就去找個字符對應的節點中繼續匹配
return this.match(map.get(letterChar), word, index + 1);
} else {
// 遍歷 下一個字符的集合
// 若是 從下一個字符繼續匹配,只要匹配成功就返回 true
for (const key of map.keys())
if (this.match(map.get(key), word, index + 1)) return true;
// 遍歷一遍以後仍是沒有匹配成功 那麼就算匹配失敗
return false;
}
}
// 獲取字典樹中存儲的單詞數量
getSize() {
return this.size;
}
// 獲取字典樹中是否爲空
isEmpty() {
return this.size === 0;
}
}
複製代碼
677. 鍵值映射
https://leetcode-cn.com/problems/map-sum-pairs/
MapSum
// 答題
class Solution {
// leetcode 677. 鍵值映射
MapSum() {
// 數組版
function ArrayVersion() {
var TrieNode = function(value) {
this.value = value;
this.next = new Array(26);
};
/** * Initialize your data structure here. */
var MapSum = function() {
this.root = new TrieNode(0);
};
/** * @param {string} key * @param {number} val * @return {void} */
MapSum.prototype.insert = function(key, val) {
this.__insert(this.root, key, val, 0);
};
MapSum.prototype.__insert = function(node, word, value, index) {
if (index === word.length) {
node.value = value;
return;
}
const array = node.next;
const i = word[index].charCodeAt(0) - 97;
if (!array[i]) array[i] = new TrieNode(0);
this.__insert(array[i], word, value, index + 1);
};
/** * @param {string} prefix * @return {number} */
MapSum.prototype.sum = function(prefix) {
// 先進行前綴匹配
let cur = this.root;
for (const c of prefix) {
const index = c.charCodeAt(0) - 97;
if (!cur.next[index]) return 0;
cur = cur.next[index];
}
// 前綴匹配成功以後 進行剩餘單詞的匹配 求和
return this.__sum(cur);
};
MapSum.prototype.__sum = function(node) {
let result = node.value || 0;
for (const next of node.next) {
if (!next) continue;
result += this.__sum(next);
}
return result;
};
/** * Your MapSum object will be instantiated and called as such: * var obj = Object.create(MapSum).createNew() * obj.insert(key,val) * var param_2 = obj.sum(prefix) */
return new MapSum();
}
// 映射版
function MapVersion() {
var TrieNode = function(value) {
this.value = value;
this.next = new Map();
};
/** * Initialize your data structure here. */
var MapSum = function() {
this.root = new TrieNode();
};
/** * @param {string} key * @param {number} val * @return {void} */
MapSum.prototype.insert = function(key, val) {
let cur = this.root;
for (const c of key) {
const map = cur.next;
if (!map.has(c)) map.set(c, new TrieNode());
cur = map.get(c);
}
cur.value = val;
};
/** * @param {string} prefix * @return {number} */
MapSum.prototype.sum = function(prefix) {
// 先處理前綴部分
let cur = this.root;
for (const c of prefix) {
const map = cur.next;
if (!map.has(c)) return 0;
cur = map.get(c);
}
return this.__sum(cur);
};
MapSum.prototype.__sum = function(node) {
let result = node.value || 0;
const map = node.next;
const keys = map.keys();
for (const key of keys) result += this.__sum(map.get(key));
return result;
};
/** * Your MapSum object will be instantiated and called as such: * var obj = Object.create(MapSum).createNew() * obj.insert(key,val) * var param_2 = obj.sum(prefix) */
return new MapSum();
}
// return new ArrayVersion();
return new MapVersion();
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('leetcode 677. 鍵值映射');
let s = new Solution();
let trie = s.MapSum();
this.show(trie.insert('apple', 3) + ' 輸出: Null');
this.show(trie.sum('ap') + ' 輸出: 3');
this.show(trie.insert('app', 2) + ' 輸出: Null');
this.show(trie.sum('ap') + ' 輸出: 5');
}
// 將內容顯示在頁面上
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: MyTrieUpgrade, class: MyTrieMap, class: Main)
MyTrie
// 自定義字典樹節點升級版 TrieNodeUpgrade
class MyTrieNodeUpgrade {
constructor(letterChar, element, isWord = false) {
this.letterChar = letterChar;
this.element = element; // 升級後能夠存儲特殊數據
this.isWord = isWord; // 是不是單詞
this.next = new Map(); // 存儲 字符所對應的節點的 字典映射
}
}
// 自定義字典樹升級版 TrieUpgrade
class MyTrieUpgrade {
constructor() {
this.root = new MyTrieNodeUpgrade();
this.size = 0;
}
add(word, element) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNodeUpgrade(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
// 存儲 額外信息
cur.element = element;
this.size++;
}
}
// 向Trie中添加一個新的單詞word 而且在word中存儲額外的信息,若是額外信息存在就覆蓋
put(word, element) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNodeUpgrade(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
this.size++;
}
// 設置或者覆蓋 額外信息
cur.element = element;
}
// 向Trie中添加一個新的單詞word 遞歸算法
// 而且在word中存儲額外的信息,若是額外信息存在就覆蓋
recursivePut(word, element) {
this.recursiveAddFn(this.root, word, element, 0);
}
// 向Trie中添加一個新的單詞word 遞歸輔助函數 -
// 而且在word中存儲額外的信息,若是額外信息存在就覆蓋
recursivePutFn(node, word, element, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) {
if (!node.isWord) {
node.isWord = true;
this.size++;
}
// 設置或者覆蓋 額外信息
node.element = element;
return;
}
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空就添加
if (!map.has(letterChar))
map.set(letterChar, new MyTrieNodeUpgrade(letterChar));
this.recursiveAddFn(map.get(letterChar), word, element, index + 1);
}
// 根據這個單詞來獲取額外信息
get(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
if (cur.isWord) return cur.element;
return null;
}
// 獲取與這個單詞前綴相關的 全部額外信息
getPrefixAll(prefix) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of prefix) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return null;
// 遊標切換到這個節點
cur = node;
}
// 前綴遍歷完畢 說明這個前綴有單詞與之匹配
// 開始進行獲取與這個前綴相關的全部單詞及其額外信息
// 將這些單詞和額外信息以 {word1 : elemnt1, word2 : element2} 形式存儲並返回
return this.recursiveGetPrefixAllInfo(cur, prefix, {});
}
// 獲取與這個單詞前綴相關的 全部額外信息 遞歸算法 -
recursiveGetPrefixAllInfo(node, word, result) {
if (node.isWord) result[word] = node.element;
const map = node.next;
const keys = map.keys();
for (const key of keys) {
this.recursiveGetPrefixAllInfo(
map.get(key),
word.concat(key),
result
);
}
return result;
}
// 獲取與這個單詞前綴相關的 帶有層次結構的全部額外信息 遞歸算法 -
recursiveGetPrefixAllTreeInfo(node, word) {
const result = [];
if (node.isWord) result.push({ word: node.element });
const map = node.next;
const keys = map.keys();
for (const key of keys)
result.push(
this.recursiveGetPrefixAll(
map.get(key),
word.concat(node.letterChar)
)
);
return result;
}
// 查詢單詞word是否在Trie中
contains(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
// 返回最後一個字符是不是一個單詞的結尾
return cur.isWord;
}
// 查詢單詞word是否在Trie中 遞歸算法
recursiveContains(word) {
return this.recursiveContainsFn(this.root, word, 0);
}
// 查詢單詞word是否在Trie中 遞歸賦值函數 -
recursiveContainsFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空那麼就說明這個單詞沒有進行存儲
if (!map.has(letterChar)) return false;
return this.recursiveContainsFn(map.get(letterChar), word, index + 1);
}
// 查詢在Trie中是否有單詞以 prefix 爲前綴
isPrefix(prefix) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of prefix) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return false;
// 遊標切換到這個節點
cur = node;
}
// 前綴遍歷完畢 說明這個前綴有單詞與之匹配
return true;
}
// 正則表達式 查詢單詞word是否在Trie中,目前只支持 統配符 "."
regexpSearch(regexpWord) {
return this.match(this.root, regexpWord, 0);
}
// 正則表達式 匹配單詞 遞歸算法 -
match(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 判斷這個字符是不是通配符
if (letterChar !== '.') {
// 若是映射中不包含這個字符
if (!map.has(letterChar)) return false;
// 若是映射中包含這個字符,那麼就去找個字符對應的節點中繼續匹配
return this.match(map.get(letterChar), word, index + 1);
} else {
// 遍歷 下一個字符的集合
// 若是 從下一個字符繼續匹配,只要匹配成功就返回 true
for (const key of map.keys())
if (this.match(map.get(key), word, index + 1)) return true;
// 遍歷一遍以後仍是沒有匹配成功 那麼就算匹配失敗
return false;
}
}
// 獲取字典樹中存儲的單詞數量
getSize() {
return this.size;
}
// 獲取字典樹中是否爲空
isEmpty() {
return this.size === 0;
}
}
複製代碼
MyTrieMap
// 自定義字典映射 TrieMap
class MyTrieMap {
constructor() {
this.trie = new MyTrieUpgrade();
}
// 添加操做
add(key, value) {
this.trie.add(key, value);
}
// 查詢操做
get(key) {
return this.trie.get(key);
}
// 刪除操做
remove(key) {
return null;
}
// 查看key是否存在
contains(key) {
return this.trie.contains(key);
}
// 更新操做
set(key, value) {
this.trie.set(key, value);
}
// 獲取映射Map中全部的key
getKeys() {
let items = this.trie.getPrefixAll('');
return Object.keys(items);
}
// 獲取映射Map中全部的value
getValues() {
let items = this.trie.getPrefixAll('');
return Object.values(items);
}
// 獲取映射Map中實際元素個數
getSize() {
return this.trie.getSize();
}
// 查看映射Map中是否爲空
isEmpty() {
return this.trie.isEmpty();
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('Map Comparison Area');
const n = 2000000;
const myBSTMap = new MyBinarySearchTreeMap();
const myTrieMap = new MyTrieMap();
let performanceTest1 = new PerformanceTest();
const random = Math.random;
let arr = [];
// 循環添加隨機數的值
for (let i = 0; i < n; i++) {
arr.push(i.toString());
}
this.alterLine('MyBSTMap Comparison Area');
const myBSTMapInfo = performanceTest1.testCustomFn(function() {
for (const word of arr)
myBSTMap.add(word, String.fromCharCode(word));
});
// 總毫秒數:3692
console.log(myBSTMapInfo);
this.show(myBSTMapInfo);
this.alterLine('MyTrieMap Comparison Area');
const myTrieMapInfo = performanceTest1.testCustomFn(function() {
for (const word of arr)
myTrieMap.add(word, String.fromCharCode(word));
});
// 總毫秒數:2805
console.log(myTrieMapInfo);
this.show(myTrieMapInfo);
console.log(myTrieMap.getKeys()); // 有效
console.log(myTrieMap.getValues()); // 有效
}
// 將內容顯示在頁面上
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();
};
複製代碼
Ternary Search Trie
優勢
就是每個節點只有左中右三個孩子,// d
// / | \
// / | \
// / | \
// a k z
// / | \
// / | \
// o
// / | \
// / | \
// i
// / | \
// / | \
// g
複製代碼
(class: MyTrieUpgrade, class: MyTrieMap, class: PerformanceTest, class: Main)
MyTrie
// 自定義字典樹節點升級版 TrieNodeUpgrade
class MyTrieNodeUpgrade {
constructor(letterChar, element, isWord = false) {
this.letterChar = letterChar;
this.element = element; // 升級後能夠存儲特殊數據
this.isWord = isWord; // 是不是單詞
this.next = new Map(); // 存儲 字符所對應的節點的 字典映射
}
}
// 自定義字典樹升級版 TrieUpgrade
class MyTrieUpgrade {
constructor() {
this.root = new MyTrieNodeUpgrade();
this.size = 0;
}
add(word, element) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNodeUpgrade(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
// 存儲 額外信息
cur.element = element;
this.size++;
}
}
// 向Trie中添加一個新的單詞word 而且在word中存儲額外的信息,若是額外信息存在就覆蓋
put(word, element) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 下一個字符所對應的映射是否爲空
if (!cur.next.has(c)) cur.next.set(c, new MyTrieNodeUpgrade(c));
// 切換到下一個節點
cur = cur.next.get(c);
}
// 若是當前這個單詞是一個新的單詞
if (!cur.isWord) {
// 當前這個字符是這個單詞的結尾
cur.isWord = true;
this.size++;
}
// 設置或者覆蓋 額外信息
cur.element = element;
}
// 向Trie中添加一個新的單詞word 遞歸算法
// 而且在word中存儲額外的信息,若是額外信息存在就覆蓋
recursivePut(word, element) {
this.recursiveAddFn(this.root, word, element, 0);
}
// 向Trie中添加一個新的單詞word 遞歸輔助函數 -
// 而且在word中存儲額外的信息,若是額外信息存在就覆蓋
recursivePutFn(node, word, element, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) {
if (!node.isWord) {
node.isWord = true;
this.size++;
}
// 設置或者覆蓋 額外信息
node.element = element;
return;
}
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空就添加
if (!map.has(letterChar))
map.set(letterChar, new MyTrieNodeUpgrade(letterChar));
this.recursiveAddFn(map.get(letterChar), word, element, index + 1);
}
// 從Trie中刪除一個單詞word
remove(word) {
return this.recursiveRemove(this.root, word, 0);
}
// 從Trie中刪除一個單詞word 遞歸算法 -
recursiveRemove(node, word, index) {
let element = null;
// 遞歸到底了
if (index === word.length) {
// 若是不是一個單詞,那麼直接返回 爲null的element
if (!node.isWord) return element;
element = node.element;
node.isWord = false;
this.size--;
return element;
}
const map = node.next;
const letterChar = word[index];
const nextNode = map.get(letterChar);
if (map.has(letterChar))
element = this.recursiveRemove(nextNode, word, index + 1);
if (element !== null) {
if (!nextNode.isWord && nextNode.next.size === 0)
map.delete(letterChar);
}
return element;
}
// 根據這個單詞來獲取額外信息
get(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
if (cur.isWord) return cur.element;
return null;
}
// 獲取與這個單詞前綴相關的 全部額外信息
getPrefixAll(prefix) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of prefix) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return null;
// 遊標切換到這個節點
cur = node;
}
// 前綴遍歷完畢 說明這個前綴有單詞與之匹配
// 開始進行獲取與這個前綴相關的全部單詞及其額外信息
// 將這些單詞和額外信息以 {word1 : elemnt1, word2 : element2} 形式存儲並返回
return this.recursiveGetPrefixAllInfo(cur, prefix, {});
}
// 獲取與這個單詞前綴相關的 全部額外信息 遞歸算法 -
recursiveGetPrefixAllInfo(node, word, result) {
if (node.isWord) result[word] = node.element;
const map = node.next;
const keys = map.keys();
for (const key of keys) {
this.recursiveGetPrefixAllInfo(
map.get(key),
word.concat(key),
result
);
}
return result;
}
// 獲取與這個單詞前綴相關的 帶有層次結構的全部額外信息 遞歸算法 -
recursiveGetPrefixAllTreeInfo(node, word) {
const result = [];
if (node.isWord) result.push({ word: node.element });
const map = node.next;
const keys = map.keys();
for (const key of keys)
result.push(
this.recursiveGetPrefixAll(
map.get(key),
word.concat(node.letterChar)
)
);
return result;
}
// 查詢單詞word是否在Trie中
contains(word) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of word) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return false;
// 遊標切換到這個節點
cur = node;
}
// 單詞遍歷完畢
// 返回最後一個字符是不是一個單詞的結尾
return cur.isWord;
}
// 查詢單詞word是否在Trie中 遞歸算法
recursiveContains(word) {
return this.recursiveContainsFn(this.root, word, 0);
}
// 查詢單詞word是否在Trie中 遞歸賦值函數 -
recursiveContainsFn(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 下一個字符所對應的映射是否爲空 爲空那麼就說明這個單詞沒有進行存儲
if (!map.has(letterChar)) return false;
return this.recursiveContainsFn(map.get(letterChar), word, index + 1);
}
// 查詢在Trie中是否有單詞以 prefix 爲前綴
isPrefix(prefix) {
// 指定遊標
let cur = this.root;
// 遍歷出當前單詞的每個字符
for (const c of prefix) {
// 獲取當前這個字符所對應的節點
const node = cur.next.get(c);
// 這個節點不存在,那麼就說明就沒有存儲這個字符
if (!node) return false;
// 遊標切換到這個節點
cur = node;
}
// 前綴遍歷完畢 說明這個前綴有單詞與之匹配
return true;
}
// 正則表達式 查詢單詞word是否在Trie中,目前只支持 統配符 "."
regexpSearch(regexpWord) {
return this.match(this.root, regexpWord, 0);
}
// 正則表達式 匹配單詞 遞歸算法 -
match(node, word, index) {
// 解決基本的問題,由於已經到底了
if (index === word.length) return node.isWord;
const map = node.next; // 獲取節點的next 也就是字符對應的映射
const letterChar = word[index]; // 獲取當前位置對應的單詞中的字符
// 判斷這個字符是不是通配符
if (letterChar !== '.') {
// 若是映射中不包含這個字符
if (!map.has(letterChar)) return false;
// 若是映射中包含這個字符,那麼就去找個字符對應的節點中繼續匹配
return this.match(map.get(letterChar), word, index + 1);
} else {
// 遍歷 下一個字符的集合
// 若是 從下一個字符繼續匹配,只要匹配成功就返回 true
for (const key of map.keys())
if (this.match(map.get(key), word, index + 1)) return true;
// 遍歷一遍以後仍是沒有匹配成功 那麼就算匹配失敗
return false;
}
}
// 獲取字典樹中存儲的單詞數量
getSize() {
return this.size;
}
// 獲取字典樹中是否爲空
isEmpty() {
return this.size === 0;
}
}
複製代碼
MyTrieMap
// 自定義字典映射 TrieMap
class MyTrieMap {
constructor() {
this.trie = new MyTrieUpgrade();
}
// 添加操做
add(key, value) {
this.trie.add(key, value);
}
// 查詢操做
get(key) {
return this.trie.get(key);
}
// 刪除操做
remove(key) {
return this.trie.remove(key);
}
// 查看key是否存在
contains(key) {
return this.trie.contains(key);
}
// 更新操做
set(key, value) {
this.trie.set(key, value);
}
// 獲取映射Map中全部的key
getKeys() {
let items = this.trie.getPrefixAll('');
return Object.keys(items);
}
// 獲取映射Map中全部的value
getValues() {
let items = this.trie.getPrefixAll('');
return Object.values(items);
}
// 獲取映射Map中實際元素個數
getSize() {
return this.trie.getSize();
}
// 查看映射Map中是否爲空
isEmpty() {
return this.trie.isEmpty();
}
}
複製代碼
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);
}
// 計算運行的時間,轉換爲 天-小時-分鐘-秒-毫秒
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 myBSTMap = new MyBinarySearchTreeMap();
const myTrieMap = new MyTrieMap();
let performanceTest1 = new PerformanceTest();
const random = Math.random;
let arr = [];
// 循環添加隨機數的值
for (let i = 0; i < n; i++) {
arr.push(Math.floor(n * random()).toString());
}
this.alterLine('MyBSTMap Comparison Area');
const myBSTMapInfo = performanceTest1.testCustomFn(function() {
// 添加
for (const word of arr)
myBSTMap.add(word, String.fromCharCode(word));
// 刪除
for (const word of arr) myBSTMap.remove(word);
// 查找
for (const word of arr)
if (myBSTMap.contains(word))
throw new Error("doesn't remove ok.");
});
// 總毫秒數:18703
console.log(myBSTMapInfo);
this.show(myBSTMapInfo);
this.alterLine('MyTrieMap Comparison Area');
const myTrieMapInfo = performanceTest1.testCustomFn(function() {
for (const word of arr)
myTrieMap.add(word, String.fromCharCode(word));
// 刪除
for (const word of arr) myTrieMap.remove(word);
// // 查找
for (const word of arr)
if (myTrieMap.contains(word))
throw new Error("doesn't remove ok.");
});
// 總毫秒數:8306
console.log(myTrieMapInfo);
this.show(myTrieMapInfo);
console.log(myTrieMap.getKeys()); // 有效
console.log(myTrieMap.getValues()); // 有效
}
// 將內容顯示在頁面上
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();
};
複製代碼