【從蛋殼到滿天飛】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 (一個一個的工程)git
所有源代碼已上傳 github,點擊我吧,光看文章可以掌握兩成,動手敲代碼、動腦思考、畫圖才能夠掌握八成。github
本文章適合 對數據結構想了解而且感興趣的人羣,文章風格一如既往如此,就以爲手機上看起來比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時間跨度也算將近半年時間了,但願對想學習數據結構的人或者正在學習數據結構的人羣有幫助。算法
哈希表相對於以前實現的那些數據結構來講數組
經過 leetcode 上的題目來看哈希表緩存
// 答題
class Solution {
// leetcode 387. 字符串中的第一個惟一字符
firstUniqChar(s) {
/** * @param {string} s * @return {number} */
var firstUniqChar = function(s) {
const hashTable = new Array(26);
for (var i = 0; i < hashTable.length; i++) hashTable[i] = 0;
for (const c of s) hashTable[c.charCodeAt(0) - 97]++;
for (var i = 0; i < hashTable.length; i++)
if (hashTable[s[i].charCodeAt(0) - 97] === 1) return i;
return -1;
};
/** * @param {string} s * @return {number} */
var firstUniqChar = function(s) {
const hashTable = new Array(26);
const letterTable = {};
for (var i = 0; i < hashTable.length; i++) {
letterTable[String.fromCharCode(i + 97)] = i;
hashTable[i] = 0;
}
for (const c of s) hashTable[letterTable[c]]++;
for (var i = 0; i < s.length; i++)
if (hashTable[letterTable[s[i]]] === 1) return i;
return -1;
};
return firstUniqChar(s);
}
}
複製代碼
哈希表是對於你所關注的內容將它轉化成索引安全
fn(char1) = char1 -'a'
,char1 -'a'
,編號-1
鍵
經過哈希函數這種狀況下不少時候就不得不處理一個在哈希表中很是關鍵的問題數據結構
哈希表充分的體現了算法設計領域的經典思想dom
O(1)
的時間完成各項操做,O(n)
級別。O(1)
的時間完成,O(n)
的時間完成。對哈希表總體來講這個數組能開多大空間是很是重要的ide
鍵
經過哈希函數獲得索引
後,哈希表這種數據結構
對於哈希表來講,關心的主要有兩部份內容
哈希函數的設計
鍵
經過哈希函數獲得的索引
分佈越均勻越好。最通常的哈希函數設計原則
-100~100
,讓它們都加 100,0~200
就能夠了,很是容易。mod 10000
,mod 1000000
,取模的數字選擇很重要,
http://planetmath.org/goodhashtableprimes
,// 10 % 4 ---> 2 10 % 7 --->3
// 20 % 4 ---> 0 20 % 7 --->6
// 30 % 4 ---> 2 30 % 7 --->2
// 40 % 4 ---> 0 40 % 7 --->4
// 50 % 4 ---> 2 50 % 7 --->1
複製代碼
浮點型的哈希函數設計
將浮點型的數據轉化爲一個整數的索引,
在計算機中都 32 位或者 64 位的二進制表示,只不過計算機解析成了浮點數,
若是鍵是浮點型的話,那麼就可使用浮點型所存儲的這個空間,
把它看成是整型來進行處理,
也就是把這個浮點型所佔用的 32 位空間或 64 位空間使用整數的方式來解析,
那麼這篇空間一樣能夠能夠表示一個整數,
以後就能夠將一個大的整數轉成整數相應的方式,也就是取模的方式,
這樣就解決了浮點型的哈希函數的設計的問題
// // 單精度
// 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
// //雙進度
// 11-bit 52-bit
// 0|011111111100|0100000000000000000000000000000000000000000000000000
// 63 52 0
複製代碼
字符串的哈希函數設計
166 = 1 * 10^2 + 6 * 10^1 + 6 * 10^0
,code = c * 26^3 + o * 26^2 + d * 26^1 + e * 26^0
,code = c * B^3 + o * B^2 + d * B^1 + e * B^0
,hash(code) = (c * B^3 + o * B^2 + d * B^1 + e * B^0) % M
,hash(code) = ((((c * B) + o) * B + d) * B + e) % M
,hash(code) = ((((c % M) * B + o) % M * B + d) % M * B + e) % M
,//hash(code) = ((((c % M) * B + o) % M * B + d) % M * B + e) % M
// 上面的公式中 ((((c % M) * B + o) % M * B + d) % M * B + e) % M
// 對應下面的代碼,只須要一重for循環便可,最終的到的就是整個字符串的哈希值
let s = 'code';
let hash = 0;
for (let i = 0; i < s.length; i++) hash = (hash * B + s.charAt(i)) % M;
複製代碼
複合類型的哈希函數設計
hash(code) = ((((c % M) * B + o) % M * B + d) % M * B + e) % M
,Date:year,month,day,
hash(date) = ((((date.year%M) * B + date.month) % M * B + date.day) % M * B + e) % M
,哈希函數設計通常來講對任何數據類型都是將它轉換成整型來處理。
在 js 中自定義數據類型
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-jwl
hashCode() {
// 選擇進制
const B = 31;
// 計算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-jwl
equals(obj) {
// 三重判斷
if (!obj) return false;
if (this === obj) return true;
if (this.valueOf() !== obj.valueOf()) return false;
// 對屬性進行判斷
return (
this.name === obj.name &&
this.score === obj.score &&
this.grade === obj.grade &&
this.classId === obj.classId
);
}
// 拆分字符生成數字 -
getCode(s) {
s = s + '';
let result = 0;
// 遍歷字符 計算結果
for (const c of s) result += c.charCodeAt(0);
// 返回結果
return result;
}
//@Override toString 2018-10-19-jwl
toString() {
let studentInfo = `Student(name: ${this.name}, score: ${this.score})`;
return studentInfo;
}
}
複製代碼
Main
// main 函數
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());
}
// 將內容顯示在頁面上
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();
};
複製代碼
(hashCode(k1) & 0x7fffffff) % M
,1111
,111
,0x7fffffff
表示的二進制就是 31 個 1,hash 值對 31 個 1 進行一下按位與,http://planetmath.org/goodhashtableprimes
,MyHashTable
// 自定義的hash生成類。
class MyHash {
constructor() {
this.store = new Map();
}
// 生成hash
hashCode(key) {
let hash = this.store.get(key);
if (hash !== undefined) return hash;
else {
// 若是 這個hash沒有進行保存 就生成,而且記錄
let hash = this.calcHashTwo(key);
// 記錄
this.store.set(key, hash);
// 返回hash
return hash;
}
}
// 獲得的數字比較小 六七位數 如下 輔助函數:生成hash -
calcHashOne(key) {
// 生成hash 隨機小數 * 當前日期毫秒 * 隨機小數
let hash = Math.random() * Date.now() * Math.random();
// hash 取小數部分的字符串
hash = hash.toString().replace(/^\d*\.\d*?([1-9]+)$/, '$1');
hash = parseInt(hash); // 取整
return hash;
}
// 獲得的數字很大 十幾位數 左右 輔助函數:生成hash -
calcHashTwo(key) {
// 生成hash 隨機小數 * 當前日期毫秒 * 隨機小數
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; // 實際元素個數
this.hashTable = new Array(M); // 哈希表
this.hashCalc = new MyHash(); // 哈希值計算
// 初始化哈希表
for (var i = 0; i < M; i++) {
// this.hashTable[i] = new MyAVLTree();
this.hashTable[i] = new Map();
}
}
// 根據key生成 哈希表索引
hash(key) {
// 獲取哈希值
let hash = this.hashCalc.hashCode(key);
// 對哈希值轉換爲32位的整數 再進行取模運算
return (hash & 0x7fffffff) % this.M;
}
// 獲取實際存儲的元素個數
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 基於使系統的Map 底層是哈希表+紅黑樹
// 自定義的哈希表 HashTable 基於本身的AVL樹
class MyHashTableByAVLTree {
constructor(M = 97) {
this.M = M; // 空間大小
this.size = 0; // 實際元素個數
this.hashTable = new Array(M); // 哈希表
this.hashCalc = new MyHash(); // 哈希值計算
// 初始化哈希表
for (var i = 0; i < M; i++) {
// this.hashTable[i] = new MyAVLTree();
this.hashTable[i] = new MyAVLTreeMap();
}
}
// 根據key生成 哈希表索引
hash(key) {
// 獲取哈希值
let hash = this.hashCalc.hashCode(key);
// 對哈希值轉換爲32位的整數 再進行取模運算
return (hash & 0x7fffffff) % this.M;
}
// 獲取實際存儲的元素個數
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);
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('HashTable Comparison Area');
const n = 2000000;
const random = Math.random;
let arrNumber = new Array(n);
// 循環添加隨機數的值
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.");
});
// 總毫秒數:
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.");
});
// 總毫秒數:
console.log(hashTableInfo1);
console.log(hashTable1);
this.show(hashTableInfo1);
}
// 將內容顯示在頁面上
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();
};
複製代碼
O(N/M)
級別,O(log(N/M))
級別,O(1)
級別的,O(1)
級別。O(1)
級別。N / M >= upperTolerance
的時候,也就是設置一個上界,N / M
大於等於 10,那麼就進行擴容操做。N / M < lowerTolerance
的時候,也就是設置一個下限,N / M
小於 2,那麼就進行縮容操做。MyHashTable
// 自定義的hash生成類。
class MyHash {
constructor() {
this.store = new Map();
}
// 生成hash
hashCode(key) {
let hash = this.store.get(key);
if (hash !== undefined) return hash;
else {
// 若是 這個hash沒有進行保存 就生成,而且記錄
let hash = this.calcHashTwo(key);
// 記錄
this.store.set(key, hash);
// 返回hash
return hash;
}
}
// 獲得的數字比較小 六七位數 如下 輔助函數:生成hash -
calcHashOne(key) {
// 生成hash 隨機小數 * 當前日期毫秒 * 隨機小數
let hash = Math.random() * Date.now() * Math.random();
// hash 取小數部分的字符串
hash = hash.toString().replace(/^\d*\.\d*?([1-9]+)$/, '$1');
hash = parseInt(hash); // 取整
return hash;
}
// 獲得的數字很大 十幾位數 左右 輔助函數:生成hash -
calcHashTwo(key) {
// 生成hash 隨機小數 * 當前日期毫秒 * 隨機小數
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; // 實際元素個數
this.hashTable = new Array(M); // 哈希表
this.hashCalc = new MyHash(); // 哈希值計算
// 初始化哈希表
for (var i = 0; i < M; i++) {
// this.hashTable[i] = new MyAVLTree();
this.hashTable[i] = new Map();
}
}
// 根據key生成 哈希表索引
hash(key) {
// 獲取哈希值
let hash = this.hashCalc.hashCode(key);
// 對哈希值轉換爲32位的整數 再進行取模運算
return (hash & 0x7fffffff) % this.M;
}
// 獲取實際存儲的元素個數
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; // 實際元素個數
this.hashTable = new Array(M); // 哈希表
this.hashCalc = new MyHash(); // 哈希值計算
// 初始化哈希表
for (var i = 0; i < M; i++) {
// this.hashTable[i] = new MyAVLTree();
this.hashTable[i] = new MyAVLTreeMap();
}
// 設定擴容的上邊界
this.upperTolerance = 10;
// 設定縮容的下邊界
this.lowerTolerance = 2;
// 初始容量大小爲 97
this.initCapcity = 97;
}
// 根據key生成 哈希表索引
hash(key) {
// 獲取哈希值
let hash = this.hashCalc.hashCode(key);
// 對哈希值轉換爲32位的整數 再進行取模運算
return (hash & 0x7fffffff) % this.M;
}
// 獲取實際存儲的元素個數
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++;
// 平均元素個數 大於等於 當前容量的10倍
// 擴容就翻倍
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--;
// 平均元素個數 小於容量的2倍 固然不管怎麼縮容,縮容以後都要大於初始容量
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++) {
// // 獲取全部實例
// map = this.hashTable[i];
// keys = map.getKeys();
// // 遍歷每一對鍵值對 實例
// 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
);
}
// 從新設置當前hashTable
this.hashTable = newHashTable;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('HashTable Comparison Area');
const n = 2000000;
const random = Math.random;
let arrNumber = new Array(n);
// 循環添加隨機數的值
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.");
});
// 總毫秒數:
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.");
});
// 總毫秒數:
console.log('HashTableBySystem' + ':' + hashTableInfo1);
console.log(hashTable1);
this.show('HashTableBySystem' + ':' + hashTableInfo1);
}
// 將內容顯示在頁面上
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();
};
複製代碼
O(n)
的複雜度,O(1)
級別的操做以後纔有這一次O(n)
級別的操做,O(n)
級別的操做平攤到 n 次O(1)
級別的操做中,O(2)
級別的操做,O(1)
級別的。O(n)
級別的操做,9*n
次操做中去,O(1)
級別的,O(lowerTolerance)~O(upperTolerance)之間
,O(1)
級別的,O(1)
級別的時間複雜度是均攤獲得的,是平均的時間複雜度。M -> 2*M
,就算初始的 M 是一個素數,http://planetmath.org/goodhashtableprimes
,// lwr upr % err prime
// 2^5 2^6 10.416667 53
// 2^6 2^7 1.041667 97
// 2^7 2^8 0.520833 193
// 2^8 2^9 1.302083 389
// 2^9 2^10 0.130208 769
// 2^10 2^11 0.455729 1543
// 2^11 2^12 0.227865 3079
// 2^12 2^13 0.113932 6151
// 2^13 2^14 0.008138 12289
// 2^14 2^15 0.069173 24593
// 2^15 2^16 0.010173 49157
// 2^16 2^17 0.013224 98317
// 2^17 2^18 0.002543 196613
// 2^18 2^19 0.006358 393241
// 2^19 2^20 0.000127 786433
// 2^20 2^21 0.000318 1572869
// 2^21 2^22 0.000350 3145739
// 2^22 2^23 0.000207 6291469
// 2^23 2^24 0.000040 12582917
// 2^24 2^25 0.000075 25165843
// 2^25 2^26 0.000010 50331653
// 2^26 2^27 0.000023 100663319
// 2^27 2^28 0.000009 201326611
// 2^28 2^29 0.000001 402653189
// 2^29 2^30 0.000011 805306457
// 2^30 2^31 0.000000 1610612741
複製代碼
2.0 * 10^9
左右,1.6\*10^9
,MyHashTable
// 自定義的hash生成類。
class MyHash {
constructor() {
this.store = new Map();
}
// 生成hash
hashCode(key) {
let hash = this.store.get(key);
if (hash !== undefined) return hash;
else {
// 若是 這個hash沒有進行保存 就生成,而且記錄
let hash = this.calcHashTwo(key);
// 記錄
this.store.set(key, hash);
// 返回hash
return hash;
}
}
// 獲得的數字比較小 六七位數 如下 輔助函數:生成hash -
calcHashOne(key) {
// 生成hash 隨機小數 * 當前日期毫秒 * 隨機小數
let hash = Math.random() * Date.now() * Math.random();
// hash 取小數部分的字符串
hash = hash.toString().replace(/^\d*\.\d*?([1-9]+)$/, '$1');
hash = parseInt(hash); // 取整
return hash;
}
// 獲得的數字很大 十幾位數 左右 輔助函數:生成hash -
calcHashTwo(key) {
// 生成hash 隨機小數 * 當前日期毫秒 * 隨機小數
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; // 實際元素個數
this.hashTable = new Array(M); // 哈希表
this.hashCalc = new MyHash(); // 哈希值計算
// 初始化哈希表
for (var i = 0; i < M; i++) {
// this.hashTable[i] = new MyAVLTree();
this.hashTable[i] = new Map();
}
}
// 根據key生成 哈希表索引
hash(key) {
// 獲取哈希值
let hash = this.hashCalc.hashCode(key);
// 對哈希值轉換爲32位的整數 再進行取模運算
return (hash & 0x7fffffff) % this.M;
}
// 獲取實際存儲的元素個數
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
// 基於本身實現的AVL樹
class MyHashTableByAVLTree {
constructor() {
// 設定擴容的上邊界
this.upperTolerance = 10;
// 設定縮容的下邊界
this.lowerTolerance = 2;
// 哈希表合理的素數表
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; // 實際元素個數
this.hashTable = new Array(this.M); // 哈希表
this.hashCalc = new MyHash(); // 哈希值計算
// 初始化哈希表
for (var i = 0; i < this.M; i++) {
// this.hashTable[i] = new MyAVLTree();
this.hashTable[i] = new MyAVLTreeMap();
}
}
// 根據key生成 哈希表索引
hash(key) {
// 獲取哈希值
let hash = this.hashCalc.hashCode(key);
// 對哈希值轉換爲32位的整數 再進行取模運算
return (hash & 0x7fffffff) % this.M;
}
// 獲取實際存儲的元素個數
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++;
// 平均元素個數 大於等於 當前容量的10倍,同時防止索引越界
// 就以哈希表合理的素數表 爲標準進行 索引的推移
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--;
// 平均元素個數 小於容量的2倍 固然不管怎麼縮容,索引都不能越界
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++) {
// // 獲取全部實例
// map = this.hashTable[i];
// keys = map.getKeys();
// // 遍歷每一對鍵值對 實例
// 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
);
}
// 從新設置當前hashTable
this.hashTable = newHashTable;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('HashTable Comparison Area');
const n = 2000000;
const random = Math.random;
let arrNumber = new Array(n);
// 循環添加隨機數的值
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.");
});
// 總毫秒數:13249
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.");
});
// 總毫秒數:5032
console.log('HashTableBySystem' + ':' + hashTableInfo1);
console.log(hashTable1);
this.show('HashTableBySystem' + ':' + hashTableInfo1);
}
// 將內容顯示在頁面上
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();
};
複製代碼
O(1)
O(logn)
,而哈希表的時間複雜度是O(1)
,hash(x) = x % 10
,25 % 10
值爲 5,那它就放到數組中索引爲 5 的位置,1 4 9 16
分別是1^2 2^2 3^2 4^2
,x^2 - (x-1)^2 = 2x - 1
,x 是1 2 3 4
,O(1)
。