forked from XPoet/js-data-structure-and-algorithm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hashTable.js
185 lines (147 loc) · 4.43 KB
/
hashTable.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* 设计哈希函数,将传入的字符串哈希化,转换成 hashCode
* @param string 要哈希化的字符串
* @param limit 哈希表的最大个数(数组长度)
* @returns {number} hashCode
*/
export function hashFn(string, limit = 7) {
// 自己采用的一个质数(无强制要求,质数即可)
const PRIME = 31;
// 1、定义存储 hashCode 的变量
let hashCode = 0;
// 2、使用霍纳法则(秦九韶算法),计算 hashCode 的值
for (let item of string) {
hashCode = PRIME * hashCode + item.charCodeAt();
}
// 3、对 hashCode 取余,并返回
return hashCode % limit;
}
/**
* 判断一个数是否为质数
* @param number
* @returns {boolean}
*/
// 方法一,性能比较低
// export function isPrime(number) {
// if (number <= 1) return false;
// for (let i = 2; i < number; i++) {
// if (number % i === 0) {
// return false;
// }
// }
// return true;
// }
// 方法二,性能较好
export function isPrime(number) {
if (number <= 1 || number === 4) return false;
const temp = Math.ceil(Math.sqrt(number));
for (let i = 2; i < temp; i++) {
if (number % i === 0) {
return false;
}
}
return true;
}
// 哈希表的封装
export class HashTable {
constructor() {
this.storage = []; // 哈希表存储数据的变量
this.count = 0; // 当前存放的元素个数
this.limit = 7; // 哈希表长度(初始设为质数 7)
// 装填因子(已有个数/总个数)
this.loadFactor = 0.75;
this.minLoadFactor = 0.25;
}
// getPrime(number) 根据传入的 number 获取最临近的质数
getPrime(number) {
while (!isPrime(number)) {
number++;
}
return number;
}
// put(key, value) 往哈希表里添加数据
put(key, value) {
// 1、根据 key 获取要映射到 storage 里面的 index(通过哈希函数获取)
const index = hashFn(key, this.limit);
// 2、根据 index 取出对应的 bucket
let bucket = this.storage[index];
// 3、判断是否存在 bucket
if (bucket === undefined) {
bucket = []; // 不存在则创建
this.storage[index] = bucket;
}
// 4、判断是插入数据操作还是修改数据操作
for (let i = 0; i < bucket.length; i++) {
let tuple = bucket[i]; // tuple 的格式:[key, value]
if (tuple[0] === key) { // 如果 key 相等,则修改数据
tuple[1] = value;
return; // 修改完 tuple 里数据,return 终止,不再往下执行。
}
}
// 5、bucket 新增数据
bucket.push([key, value]); // bucket 存储元组 tuple,格式为 [key, value]
this.count++;
// 判断哈希表是否要扩容,若装填因子 > 0.75,则扩容
if (this.count / this.limit > this.loadFactor) {
this.resize(this.getPrime(this.limit * 2));
}
}
// 根据 get(key) 获取 value
get(key) {
const index = hashFn(key, this.limit);
const bucket = this.storage[index];
if (bucket === undefined) {
return null;
}
for (const tuple of bucket) {
if (tuple[0] === key) {
return tuple[1];
}
}
return null;
}
// remove(key) 删除指定 key 的数据
remove(key) {
const index = hashFn(key, this.limit);
const bucket = this.storage[index];
if (bucket === undefined) {
return null;
}
// 遍历 bucket,找到对应位置的 tuple,将其删除
for (let i = 0, len = bucket.length; i < len; i++) {
const tuple = bucket[i];
if (tuple[0] === key) {
bucket.splice(i, 1); // 删除对应位置的数组项
this.count--;
// 根据装填因子的大小,判断是否要进行哈希表压缩
if (this.limit > 7 && this.count / this.limit < this.minLoadFactor) {
this.resize(this.getPrime(Math.floor(this.limit / 2)));
}
return tuple;
}
}
}
isEmpty() {
return this.count === 0;
}
size() {
return this.count;
}
// 重新调整哈希表大小,扩容或压缩
resize(newLimit) {
// 1、保存旧的 storage 数组内容
const oldStorage = this.storage;
// 2、重置所有属性
this.storage = [];
this.count = 0;
this.limit = newLimit;
// 3、遍历 oldStorage,取出所有数据,重新 put 到 this.storage
for (const bucket of oldStorage) {
if (bucket) {
for (const b of bucket) {
this.put(b[0], b[1]);
}
}
}
}
}