【从蛋壳到满天飞】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();
};
复制代码