javascript
【从蛋壳到满天飞】JS 数据结构解析和算法实现-哈希表
前言
【從蛋殼到滿天飛】JS 數(shù)據(jù)結(jié)構(gòu)解析和算法實(shí)現(xiàn),全部文章大概的內(nèi)容如下: Arrays(數(shù)組)、Stacks(棧)、Queues(隊(duì)列)、LinkedList(鏈表)、Recursion(遞歸思想)、BinarySearchTree(二分搜索樹)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(優(yōu)先隊(duì)列)、SegmentTree(線段樹)、Trie(字典樹)、UnionFind(并查集)、AVLTree(AVL 平衡樹)、RedBlackTree(紅黑平衡樹)、HashTable(哈希表)
源代碼有三個(gè):ES6(單個(gè)單個(gè)的 class 類型的 js 文件) | JS + HTML(一個(gè) js 配合一個(gè) html)| JAVA (一個(gè)一個(gè)的工程)
全部源代碼已上傳 github,點(diǎn)擊我吧,光看文章能夠掌握兩成,動(dòng)手敲代碼、動(dòng)腦思考、畫圖才可以掌握八成。
本文章適合 對(duì)數(shù)據(jù)結(jié)構(gòu)想了解并且感興趣的人群,文章風(fēng)格一如既往如此,就覺得手機(jī)上看起來(lái)比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時(shí)間跨度也算將近半年時(shí)間了,希望對(duì)想學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的人或者正在學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的人群有幫助。
哈希表
哈希表相對(duì)于之前實(shí)現(xiàn)的那些數(shù)據(jù)結(jié)構(gòu)來(lái)說(shuō)
通過 leetcode 上的題目來(lái)看哈希表
哈希表是對(duì)于你所關(guān)注的內(nèi)容將它轉(zhuǎn)化成索引
這種情況下很多時(shí)候就不得不處理一個(gè)在哈希表中非常關(guān)鍵的問題
哈希表充分的體現(xiàn)了算法設(shè)計(jì)領(lǐng)域的經(jīng)典思想
對(duì)哈希表整體來(lái)說(shuō)這個(gè)數(shù)組能開多大空間是非常重要的
哈希函數(shù)的設(shè)計(jì)
哈希表這種數(shù)據(jù)結(jié)構(gòu)
對(duì)于哈希表來(lái)說(shuō),關(guān)心的主要有兩部分內(nèi)容
哈希函數(shù)的設(shè)計(jì)
最一般的哈希函數(shù)設(shè)計(jì)原則
取模的數(shù)字選擇很重要,
浮點(diǎn)型的哈希函數(shù)設(shè)計(jì)
將浮點(diǎn)型的數(shù)據(jù)轉(zhuǎn)化為一個(gè)整數(shù)的索引,
在計(jì)算機(jī)中都 32 位或者 64 位的二進(jìn)制表示,只不過計(jì)算機(jī)解析成了浮點(diǎn)數(shù),
如果鍵是浮點(diǎn)型的話,那么就可以使用浮點(diǎn)型所存儲(chǔ)的這個(gè)空間,
把它當(dāng)作是整型來(lái)進(jìn)行處理,
也就是把這個(gè)浮點(diǎn)型所占用的 32 位空間或 64 位空間使用整數(shù)的方式來(lái)解析,
那么這篇空間同樣可以可以表示一個(gè)整數(shù),
之后就可以將一個(gè)大的整數(shù)轉(zhuǎn)成整數(shù)相應(yīng)的方式,也就是取模的方式,
這樣就解決了浮點(diǎn)型的哈希函數(shù)的設(shè)計(jì)的問題
// // 單精度 // 8-bit 23-bit // 0 | 0 1 1 1 1 1 0 0 | 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 // 31 23 0// //雙進(jìn)度 // 11-bit 52-bit // 0|011111111100|0100000000000000000000000000000000000000000000000000 // 63 52 0 復(fù)制代碼字符串的哈希函數(shù)設(shè)計(jì)
復(fù)合類型的哈希函數(shù)設(shè)計(jì)
哈希函數(shù)設(shè)計(jì)一般來(lái)說(shuō)對(duì)任何數(shù)據(jù)類型都是將它轉(zhuǎn)換成整型來(lái)處理。
哈希函數(shù)的設(shè)計(jì),通常要遵循三個(gè)原則
js 中 自定義 hashCode 方法
在 js 中自定義數(shù)據(jù)類型
Student
// Student class Student {constructor(grade, classId, studentName, studentScore) {this.name = studentName;this.score = studentScore;this.grade = grade;this.classId = classId;}//@Override hashCode 2018-11-25-jwlhashCode() {// 選擇進(jìn)制const B = 31;// 計(jì)算hash值let hash = 0;hash = hash * B + this.getCode(this.name.toLowerCase());hash = hash * B + this.getCode(this.score);hash = hash * B + this.getCode(this.grade);hash = hash * B + this.getCode(this.classId);// 返回hash值return hash;}//@Override equals 2018-11-25-jwlequals(obj) {// 三重判斷if (!obj) return false;if (this === obj) return true;if (this.valueOf() !== obj.valueOf()) return false;// 對(duì)屬性進(jìn)行判斷return (this.name === obj.name &&this.score === obj.score &&this.grade === obj.grade &&this.classId === obj.classId);}// 拆分字符生成數(shù)字 -getCode(s) {s = s + '';let result = 0;// 遍歷字符 計(jì)算結(jié)果for (const c of s) result += c.charCodeAt(0);// 返回結(jié)果return result;}//@Override toString 2018-10-19-jwltoString() {let studentInfo = `Student(name: ${this.name}, score: ${this.score})`;return studentInfo;} } 復(fù)制代碼Main
// main 函數(shù) class Main {constructor() {// var s = "leetcode";// this.show(new Solution().firstUniqChar(s) + " =====> 返回 0.");// var s = "loveleetcode";// this.show(new Solution().firstUniqChar(s) + " =====> 返回 2.");const jwl = new Student(10, 4, 'jwl', 99);this.show(jwl.hashCode());console.log(jwl.hashCode());const jwl2 = new Student(10, 4, 'jwl', 99);this.show(jwl2.hashCode());console.log(jwl2.hashCode());}// 將內(nèi)容顯示在頁(yè)面上show(content) {document.body.innerHTML += `${content}<br /><br />`;}// 展示分割線alterLine(title) {let line = `--------------------${title}----------------------`;console.log(line);document.body.innerHTML += `${line}<br /><br />`;} }// 頁(yè)面加載完畢 window.onload = function() {// 執(zhí)行主函數(shù)new Main(); }; 復(fù)制代碼哈希沖突的處理-鏈地址法(Seperate Chaining)
實(shí)現(xiàn)自己的哈希表
代碼示例
MyHashTable
// 自定義的hash生成類。 class MyHash {constructor() {this.store = new Map();}// 生成hashhashCode(key) {let hash = this.store.get(key);if (hash !== undefined) return hash;else {// 如果 這個(gè)hash沒有進(jìn)行保存 就生成,并且記錄let hash = this.calcHashTwo(key);// 記錄this.store.set(key, hash);// 返回hashreturn hash;}}// 得到的數(shù)字比較小 六七位數(shù) 以下 輔助函數(shù):生成hash -calcHashOne(key) {// 生成hash 隨機(jī)小數(shù) * 當(dāng)前日期毫秒 * 隨機(jī)小數(shù)let hash = Math.random() * Date.now() * Math.random();// hash 取小數(shù)部分的字符串hash = hash.toString().replace(/^\d*\.\d*?([1-9]+)$/, '$1');hash = parseInt(hash); // 取整return hash;}// 得到的數(shù)字很大 十幾位數(shù) 左右 輔助函數(shù):生成hash -calcHashTwo(key) {// 生成hash 隨機(jī)小數(shù) * 當(dāng)前日期毫秒 * 隨機(jī)小數(shù)let hash = Math.random() * Date.now() * Math.random();// hash 向下取整hash = Math.floor(hash);return hash;} }class MyHashTableBySystem {constructor(M = 97) {this.M = M; // 空間大小this.size = 0; // 實(shí)際元素個(gè)數(shù)this.hashTable = new Array(M); // 哈希表this.hashCalc = new MyHash(); // 哈希值計(jì)算// 初始化哈希表for (var i = 0; i < M; i++) {// this.hashTable[i] = new MyAVLTree();this.hashTable[i] = new Map();}}// 根據(jù)key生成 哈希表索引hash(key) {// 獲取哈希值let hash = this.hashCalc.hashCode(key);// 對(duì)哈希值轉(zhuǎn)換為32位的整數(shù) 再進(jìn)行取模運(yùn)算return (hash & 0x7fffffff) % this.M;}// 獲取實(shí)際存儲(chǔ)的元素個(gè)數(shù)getSize() {return this.size;}// 添加元素add(key, value) {const map = this.hashTable[this.hash(key)];// 如果存在就覆蓋if (map.has(key)) map.set(key, value);else {// 不存在就添加map.set(key, value);this.size++;}}// 刪除元素remove(key) {const map = this.hashTable[this.hash(key)];let value = null;// 存在就刪除if (map.has(key)) {value = map.delete(key);this.size--;}return value;}// 修改操作set(key, value) {const map = this.hashTable[this.hash(key)];if (!map.has(key)) throw new Error(key + " doesn't exist!");map.set(key, value);}// 查找是否存在contains(key) {return this.hashTable[this.hash(key)].has(key);}// 查找操作get(key) {return this.hashTable[this.hash(key)].get(key);} }// 自定義的哈希表 HashTable 基于使系統(tǒng)的Map 底層是哈希表+紅黑樹 // 自定義的哈希表 HashTable 基于自己的AVL樹 class MyHashTableByAVLTree {constructor(M = 97) {this.M = M; // 空間大小this.size = 0; // 實(shí)際元素個(gè)數(shù)this.hashTable = new Array(M); // 哈希表this.hashCalc = new MyHash(); // 哈希值計(jì)算// 初始化哈希表for (var i = 0; i < M; i++) {// this.hashTable[i] = new MyAVLTree();this.hashTable[i] = new MyAVLTreeMap();}}// 根據(jù)key生成 哈希表索引hash(key) {// 獲取哈希值let hash = this.hashCalc.hashCode(key);// 對(duì)哈希值轉(zhuǎn)換為32位的整數(shù) 再進(jìn)行取模運(yùn)算return (hash & 0x7fffffff) % this.M;}// 獲取實(shí)際存儲(chǔ)的元素個(gè)數(shù)getSize() {return this.size;}// 添加元素add(key, value) {const map = this.hashTable[this.hash(key)];// 如果存在就覆蓋if (map.contains(key)) map.set(key, value);else {// 不存在就添加map.add(key, value);this.size++;}}// 刪除元素remove(key) {const map = this.hashTable[this.hash(key)];let value = null;// 存在就刪除if (map.contains(key)) {value = map.remove(key);this.size--;}return value;}// 修改操作set(key, value) {const map = this.hashTable[this.hash(key)];if (!map.contains(key)) throw new Error(key + " doesn't exist!");map.set(key, value);}// 查找是否存在contains(key) {return this.hashTable[this.hash(key)].contains(key);}// 查找操作get(key) {return this.hashTable[this.hash(key)].get(key);} } 復(fù)制代碼Main
// main 函數(shù) class Main {constructor() {this.alterLine('HashTable Comparison Area');const n = 2000000;const random = Math.random;let arrNumber = new Array(n);// 循環(huán)添加隨機(jī)數(shù)的值for (let i = 0; i < n; i++) arrNumber[i] = Math.floor(n * random());const hashTable = new MyHashTableByAVLTree(1572869);const hashTable1 = new MyHashTableBySystem(1572869);const performanceTest1 = new PerformanceTest();const that = this;const hashTableInfo = performanceTest1.testCustomFn(function() {// 添加for (const word of arrNumber)hashTable.add(word, String.fromCharCode(word));that.show('size : ' + hashTable.getSize());console.log('size : ' + hashTable.getSize());// 刪除for (const word of arrNumber) hashTable.remove(word);// 查找for (const word of arrNumber)if (hashTable.contains(word))throw new Error("doesn't remove ok.");});// 總毫秒數(shù):console.log(hashTableInfo);console.log(hashTable);this.show(hashTableInfo);const hashTableInfo1 = performanceTest1.testCustomFn(function() {// 添加for (const word of arrNumber)hashTable1.add(word, String.fromCharCode(word));that.show('size : ' + hashTable1.getSize());console.log('size : ' + hashTable1.getSize());// 刪除for (const word of arrNumber) hashTable1.remove(word);// 查找for (const word of arrNumber)if (hashTable1.contains(word))throw new Error("doesn't remove ok.");});// 總毫秒數(shù):console.log(hashTableInfo1);console.log(hashTable1);this.show(hashTableInfo1);}// 將內(nèi)容顯示在頁(yè)面上show(content) {document.body.innerHTML += `${content}<br /><br />`;}// 展示分割線alterLine(title) {let line = `--------------------${title}----------------------`;console.log(line);document.body.innerHTML += `${line}<br /><br />`;} }// 頁(yè)面加載完畢 window.onload = function() {// 執(zhí)行主函數(shù)new Main(); }; 復(fù)制代碼哈希表的動(dòng)態(tài)空間處理與復(fù)雜度分析
哈希表的時(shí)間復(fù)雜度
哈希表的動(dòng)態(tài)空間處理
代碼示例
MyHashTable
// 自定義的hash生成類。 class MyHash {constructor() {this.store = new Map();}// 生成hashhashCode(key) {let hash = this.store.get(key);if (hash !== undefined) return hash;else {// 如果 這個(gè)hash沒有進(jìn)行保存 就生成,并且記錄let hash = this.calcHashTwo(key);// 記錄this.store.set(key, hash);// 返回hashreturn hash;}}// 得到的數(shù)字比較小 六七位數(shù) 以下 輔助函數(shù):生成hash -calcHashOne(key) {// 生成hash 隨機(jī)小數(shù) * 當(dāng)前日期毫秒 * 隨機(jī)小數(shù)let hash = Math.random() * Date.now() * Math.random();// hash 取小數(shù)部分的字符串hash = hash.toString().replace(/^\d*\.\d*?([1-9]+)$/, '$1');hash = parseInt(hash); // 取整return hash;}// 得到的數(shù)字很大 十幾位數(shù) 左右 輔助函數(shù):生成hash -calcHashTwo(key) {// 生成hash 隨機(jī)小數(shù) * 當(dāng)前日期毫秒 * 隨機(jī)小數(shù)let hash = Math.random() * Date.now() * Math.random();// hash 向下取整hash = Math.floor(hash);return hash;} }class MyHashTableBySystem {constructor(M = 97) {this.M = M; // 空間大小this.size = 0; // 實(shí)際元素個(gè)數(shù)this.hashTable = new Array(M); // 哈希表this.hashCalc = new MyHash(); // 哈希值計(jì)算// 初始化哈希表for (var i = 0; i < M; i++) {// this.hashTable[i] = new MyAVLTree();this.hashTable[i] = new Map();}}// 根據(jù)key生成 哈希表索引hash(key) {// 獲取哈希值let hash = this.hashCalc.hashCode(key);// 對(duì)哈希值轉(zhuǎn)換為32位的整數(shù) 再進(jìn)行取模運(yùn)算return (hash & 0x7fffffff) % this.M;}// 獲取實(shí)際存儲(chǔ)的元素個(gè)數(shù)getSize() {return this.size;}// 添加元素add(key, value) {const map = this.hashTable[this.hash(key)];// 如果存在就覆蓋if (map.has(key)) map.set(key, value);else {// 不存在就添加map.set(key, value);this.size++;}}// 刪除元素remove(key) {const map = this.hashTable[this.hash(key)];let value = null;// 存在就刪除if (map.has(key)) {value = map.delete(key);this.size--;}return value;}// 修改操作set(key, value) {const map = this.hashTable[this.hash(key)];if (!map.has(key)) throw new Error(key + " doesn't exist!");map.set(key, value);}// 查找是否存在contains(key) {return this.hashTable[this.hash(key)].has(key);}// 查找操作get(key) {return this.hashTable[this.hash(key)].get(key);} }// 自定義的哈希表 HashTable // 自定義的哈希表 HashTable class MyHashTableByAVLTree {constructor(M = 97) {this.M = M; // 空間大小this.size = 0; // 實(shí)際元素個(gè)數(shù)this.hashTable = new Array(M); // 哈希表this.hashCalc = new MyHash(); // 哈希值計(jì)算// 初始化哈希表for (var i = 0; i < M; i++) {// this.hashTable[i] = new MyAVLTree();this.hashTable[i] = new MyAVLTreeMap();}// 設(shè)定擴(kuò)容的上邊界this.upperTolerance = 10;// 設(shè)定縮容的下邊界this.lowerTolerance = 2;// 初始容量大小為 97this.initCapcity = 97;}// 根據(jù)key生成 哈希表索引hash(key) {// 獲取哈希值let hash = this.hashCalc.hashCode(key);// 對(duì)哈希值轉(zhuǎn)換為32位的整數(shù) 再進(jìn)行取模運(yùn)算return (hash & 0x7fffffff) % this.M;}// 獲取實(shí)際存儲(chǔ)的元素個(gè)數(shù)getSize() {return this.size;}// 添加元素add(key, value) {const map = this.hashTable[this.hash(key)];// 如果存在就覆蓋if (map.contains(key)) map.set(key, value);else {// 不存在就添加map.add(key, value);this.size++;// 平均元素個(gè)數(shù) 大于等于 當(dāng)前容量的10倍// 擴(kuò)容就翻倍if (this.size >= this.upperTolerance * this.M)this.resize(2 * this.M);}}// 刪除元素remove(key) {const map = this.hashTable[this.hash(key)];let value = null;// 存在就刪除if (map.contains(key)) {value = map.remove(key);this.size--;// 平均元素個(gè)數(shù) 小于容量的2倍 當(dāng)然無(wú)論怎么縮容,縮容之后都要大于初始容量if (this.size < this.lowerTolerance * this.M &&this.M / 2 > this.initCapcity)this.resize(Math.floor(this.M / 2));}return value;}// 修改操作set(key, value) {const map = this.hashTable[this.hash(key)];if (!map.contains(key)) throw new Error(key + " doesn't exist!");map.set(key, value);}// 查找是否存在contains(key) {return this.hashTable[this.hash(key)].contains(key);}// 查找操作get(key) {return this.hashTable[this.hash(key)].get(key);}// 重置空間大小resize(newM) {// 初始化新空間const newHashTable = new Array(newM);for (var i = 0; i < newM; i++) newHashTable[i] = new MyAVLTree();const oldM = this.M;this.M = newM;// 方式一// let map;// let keys;// for (var i = 0; i < oldM; i++) {// // 獲取所有實(shí)例// map = this.hashTable[i];// keys = map.getKeys();// // 遍歷每一對(duì)鍵值對(duì) 實(shí)例// for(const key of keys)// newHashTable[this.hash(key)].add(key, map.get(key));// }// 方式二let etities;for (var i = 0; i < oldM; i++) {etities = this.hashTable[i].getEntitys();for (const entity of etities)newHashTable[this.hash(entity.key)].add(entity.key,entity.value);}// 重新設(shè)置當(dāng)前hashTablethis.hashTable = newHashTable;} } 復(fù)制代碼Main
// main 函數(shù) class Main {constructor() {this.alterLine('HashTable Comparison Area');const n = 2000000;const random = Math.random;let arrNumber = new Array(n);// 循環(huán)添加隨機(jī)數(shù)的值for (let i = 0; i < n; i++) arrNumber[i] = Math.floor(n * random());this.alterLine('HashTable Comparison Area');const hashTable = new MyHashTableByAVLTree();const hashTable1 = new MyHashTableBySystem();const performanceTest1 = new PerformanceTest();const that = this;const hashTableInfo = performanceTest1.testCustomFn(function() {// 添加for (const word of arrNumber)hashTable.add(word, String.fromCharCode(word));that.show('size : ' + hashTable.getSize());console.log('size : ' + hashTable.getSize());// 刪除for (const word of arrNumber) hashTable.remove(word);// 查找for (const word of arrNumber)if (hashTable.contains(word))throw new Error("doesn't remove ok.");});// 總毫秒數(shù):console.log('HashTableByAVLTree' + ':' + hashTableInfo);console.log(hashTable);this.show('HashTableByAVLTree' + ':' + hashTableInfo);const hashTableInfo1 = performanceTest1.testCustomFn(function() {// 添加for (const word of arrNumber)hashTable1.add(word, String.fromCharCode(word));that.show('size : ' + hashTable1.getSize());console.log('size : ' + hashTable1.getSize());// 刪除for (const word of arrNumber) hashTable1.remove(word);// 查找for (const word of arrNumber)if (hashTable1.contains(word))throw new Error("doesn't remove ok.");});// 總毫秒數(shù):console.log('HashTableBySystem' + ':' + hashTableInfo1);console.log(hashTable1);this.show('HashTableBySystem' + ':' + hashTableInfo1);}// 將內(nèi)容顯示在頁(yè)面上show(content) {document.body.innerHTML += `${content}<br /><br />`;}// 展示分割線alterLine(title) {let line = `--------------------${title}----------------------`;console.log(line);document.body.innerHTML += `${line}<br /><br />`;} }// 頁(yè)面加載完畢 window.onload = function() {// 執(zhí)行主函數(shù)new Main(); }; 復(fù)制代碼哈希表更復(fù)雜的動(dòng)態(tài)空間處理方法
哈希表的復(fù)雜度分析
更復(fù)雜的動(dòng)態(tài)空間處理方法
代碼示例
MyHashTable
// 自定義的hash生成類。 class MyHash {constructor() {this.store = new Map();}// 生成hashhashCode(key) {let hash = this.store.get(key);if (hash !== undefined) return hash;else {// 如果 這個(gè)hash沒有進(jìn)行保存 就生成,并且記錄let hash = this.calcHashTwo(key);// 記錄this.store.set(key, hash);// 返回hashreturn hash;}}// 得到的數(shù)字比較小 六七位數(shù) 以下 輔助函數(shù):生成hash -calcHashOne(key) {// 生成hash 隨機(jī)小數(shù) * 當(dāng)前日期毫秒 * 隨機(jī)小數(shù)let hash = Math.random() * Date.now() * Math.random();// hash 取小數(shù)部分的字符串hash = hash.toString().replace(/^\d*\.\d*?([1-9]+)$/, '$1');hash = parseInt(hash); // 取整return hash;}// 得到的數(shù)字很大 十幾位數(shù) 左右 輔助函數(shù):生成hash -calcHashTwo(key) {// 生成hash 隨機(jī)小數(shù) * 當(dāng)前日期毫秒 * 隨機(jī)小數(shù)let hash = Math.random() * Date.now() * Math.random();// hash 向下取整hash = Math.floor(hash);return hash;} }class MyHashTableBySystem {constructor(M = 97) {this.M = M; // 空間大小this.size = 0; // 實(shí)際元素個(gè)數(shù)this.hashTable = new Array(M); // 哈希表this.hashCalc = new MyHash(); // 哈希值計(jì)算// 初始化哈希表for (var i = 0; i < M; i++) {// this.hashTable[i] = new MyAVLTree();this.hashTable[i] = new Map();}}// 根據(jù)key生成 哈希表索引hash(key) {// 獲取哈希值let hash = this.hashCalc.hashCode(key);// 對(duì)哈希值轉(zhuǎn)換為32位的整數(shù) 再進(jìn)行取模運(yùn)算return (hash & 0x7fffffff) % this.M;}// 獲取實(shí)際存儲(chǔ)的元素個(gè)數(shù)getSize() {return this.size;}// 添加元素add(key, value) {const map = this.hashTable[this.hash(key)];// 如果存在就覆蓋if (map.has(key)) map.set(key, value);else {// 不存在就添加map.set(key, value);this.size++;}}// 刪除元素remove(key) {const map = this.hashTable[this.hash(key)];let value = null;// 存在就刪除if (map.has(key)) {value = map.delete(key);this.size--;}return value;}// 修改操作set(key, value) {const map = this.hashTable[this.hash(key)];if (!map.has(key)) throw new Error(key + " doesn't exist!");map.set(key, value);}// 查找是否存在contains(key) {return this.hashTable[this.hash(key)].has(key);}// 查找操作get(key) {return this.hashTable[this.hash(key)].get(key);} }// 自定義的哈希表 HashTable // 基于系統(tǒng)的哈希表,用來(lái)測(cè)試 // 自定義的哈希表 HashTable // 基于自己實(shí)現(xiàn)的AVL樹 class MyHashTableByAVLTree {constructor() {// 設(shè)定擴(kuò)容的上邊界this.upperTolerance = 10;// 設(shè)定縮容的下邊界this.lowerTolerance = 2;// 哈希表合理的素?cái)?shù)表this.capacity = [53,97,193,389,769,1543,3079,6151,12289,24593,49157,98317,196613,393241,786433,1572869,3145739,6291469,12582917,25165843,50331653,100663319,201326611,402653189,805306457,1610612741];// 初始容量的索引this.capacityIndex = 0;this.M = this.capacity[this.capacityIndex]; // 空間大小this.size = 0; // 實(shí)際元素個(gè)數(shù)this.hashTable = new Array(this.M); // 哈希表this.hashCalc = new MyHash(); // 哈希值計(jì)算// 初始化哈希表for (var i = 0; i < this.M; i++) {// this.hashTable[i] = new MyAVLTree();this.hashTable[i] = new MyAVLTreeMap();}}// 根據(jù)key生成 哈希表索引hash(key) {// 獲取哈希值let hash = this.hashCalc.hashCode(key);// 對(duì)哈希值轉(zhuǎn)換為32位的整數(shù) 再進(jìn)行取模運(yùn)算return (hash & 0x7fffffff) % this.M;}// 獲取實(shí)際存儲(chǔ)的元素個(gè)數(shù)getSize() {return this.size;}// 添加元素add(key, value) {const map = this.hashTable[this.hash(key)];// 如果存在就覆蓋if (map.contains(key)) map.set(key, value);else {// 不存在就添加map.add(key, value);this.size++;// 平均元素個(gè)數(shù) 大于等于 當(dāng)前容量的10倍,同時(shí)防止索引越界// 就以哈希表合理的素?cái)?shù)表 為標(biāo)準(zhǔn)進(jìn)行 索引的推移if (this.size >= this.upperTolerance * this.M &&this.capacityIndex + 1 < this.capacity.length)this.resize(this.capacity[++this.capacityIndex]);}}// 刪除元素remove(key) {const map = this.hashTable[this.hash(key)];let value = null;// 存在就刪除if (map.contains(key)) {value = map.remove(key);this.size--;// 平均元素個(gè)數(shù) 小于容量的2倍 當(dāng)然無(wú)論怎么縮容,索引都不能越界if (this.size < this.lowerTolerance * this.M &&this.capacityIndex > 0)this.resize(this.capacity[--this.capacityIndex]);}return value;}// 修改操作set(key, value) {const map = this.hashTable[this.hash(key)];if (!map.contains(key)) throw new Error(key + " doesn't exist!");map.set(key, value);}// 查找是否存在contains(key) {return this.hashTable[this.hash(key)].contains(key);}// 查找操作get(key) {return this.hashTable[this.hash(key)].get(key);}// 重置空間大小resize(newM) {// 初始化新空間const newHashTable = new Array(newM);for (var i = 0; i < newM; i++) newHashTable[i] = new MyAVLTree();const oldM = this.M;this.M = newM;// 方式一// let map;// let keys;// for (var i = 0; i < oldM; i++) {// // 獲取所有實(shí)例// map = this.hashTable[i];// keys = map.getKeys();// // 遍歷每一對(duì)鍵值對(duì) 實(shí)例// for(const key of keys)// newHashTable[this.hash(key)].add(key, map.get(key));// }// 方式二let etities;for (var i = 0; i < oldM; i++) {etities = this.hashTable[i].getEntitys();for (const entity of etities)newHashTable[this.hash(entity.key)].add(entity.key,entity.value);}// 重新設(shè)置當(dāng)前hashTablethis.hashTable = newHashTable;} } 復(fù)制代碼Main
// main 函數(shù) class Main {constructor() {this.alterLine('HashTable Comparison Area');const n = 2000000;const random = Math.random;let arrNumber = new Array(n);// 循環(huán)添加隨機(jī)數(shù)的值for (let i = 0; i < n; i++) arrNumber[i] = Math.floor(n * random());this.alterLine('HashTable Comparison Area');const hashTable = new MyHashTableByAVLTree();const hashTable1 = new MyHashTableBySystem();const performanceTest1 = new PerformanceTest();const that = this;const hashTableInfo = performanceTest1.testCustomFn(function() {// 添加for (const word of arrNumber)hashTable.add(word, String.fromCharCode(word));that.show('size : ' + hashTable.getSize());console.log('size : ' + hashTable.getSize());// 刪除for (const word of arrNumber) hashTable.remove(word);// 查找for (const word of arrNumber)if (hashTable.contains(word))throw new Error("doesn't remove ok.");});// 總毫秒數(shù):13249console.log('HashTableByAVLTree' + ':' + hashTableInfo);console.log(hashTable);this.show('HashTableByAVLTree' + ':' + hashTableInfo);const hashTableInfo1 = performanceTest1.testCustomFn(function() {// 添加for (const word of arrNumber)hashTable1.add(word, String.fromCharCode(word));that.show('size : ' + hashTable1.getSize());console.log('size : ' + hashTable1.getSize());// 刪除for (const word of arrNumber) hashTable1.remove(word);// 查找for (const word of arrNumber)if (hashTable1.contains(word))throw new Error("doesn't remove ok.");});// 總毫秒數(shù):5032console.log('HashTableBySystem' + ':' + hashTableInfo1);console.log(hashTable1);this.show('HashTableBySystem' + ':' + hashTableInfo1);}// 將內(nèi)容顯示在頁(yè)面上show(content) {document.body.innerHTML += `${content}<br /><br />`;}// 展示分割線alterLine(title) {let line = `--------------------${title}----------------------`;console.log(line);document.body.innerHTML += `${line}<br /><br />`;} }// 頁(yè)面加載完畢 window.onload = function() {// 執(zhí)行主函數(shù)new Main(); }; 復(fù)制代碼哈希表的更多話題
更多哈希沖突的處理方法
總結(jié)
以上是生活随笔為你收集整理的【从蛋壳到满天飞】JS 数据结构解析和算法实现-哈希表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2018-2019 Exp2 后门原理与
- 下一篇: [JVM]常用JVM工具使用