forked from TheAlgorithms/TypeScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhash_map.ts
More file actions
239 lines (207 loc) · 4.56 KB
/
hash_map.ts
File metadata and controls
239 lines (207 loc) · 4.56 KB
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import { Map } from './map'
/**
* Represents a hash map.
* Time complexity:
* - Set, Get, Delete, Has: O(1) on average, O(n) in the worst case.
* - Clear: O(m) where m is the number of buckets.
* - Keys, Values, Entires: O(n + m).
*
* @template K The key type.
* @template V The value type.
* @param size The size of the hash map.
* @param buckets The buckets in which to store the key-value pairs.
* @param loadFactor The load factor to determine when to resize the hash map.
*/
export class HashMap<K, V> implements Map<K, V> {
private size!: number
private buckets!: MapEntry<K, V>[][]
private readonly loadFactor = 0.75
constructor() {
this.clear()
}
/**
* Gets the size.
*
* @returns The size.
*/
getSize(): number {
return this.size
}
/**
* Sets a key-value pair.
*
* @param key The key.
* @param value The value.
*/
set(key: K, value: V): void {
const loadFactor = this.size / this.buckets.length
if (loadFactor > this.loadFactor) {
this.resize()
}
const index = this.hash(key)
const bucket = this.buckets[index]
if (bucket.length === 0) {
bucket.push(new MapEntry(key, value))
this.size++
return
}
for (const entry of bucket) {
if (entry.key === key) {
entry.value = value
return
}
}
bucket.push(new MapEntry(key, value))
this.size++
}
/**
* Gets a value.
*
* @param key The key to get the value for.
* @returns The value or null if the key does not exist.
*/
get(key: K): V | null {
const index = this.hash(key)
const bucket = this.buckets[index]
for (const entry of bucket) {
if (entry.key === key) {
return entry.value
}
}
return null
}
/**
* Deletes a key-value pair.
*
* @param key The key whose key-value pair to delete.
*/
delete(key: K): void {
const index = this.hash(key)
const bucket = this.buckets[index]
for (const entry of bucket) {
if (entry.key === key) {
bucket.splice(bucket.indexOf(entry), 1)
this.size--
return
}
}
}
/**
* Checks if a key exists.
*
* @param key The key.
* @returns Whether the key exists.
*/
has(key: K): boolean {
const index = this.hash(key)
const bucket = this.buckets[index]
for (const entry of bucket) {
if (entry.key === key) {
return true
}
}
return false
}
/**
* Clears the hash map.
*/
clear(): void {
this.size = 0
this.initializeBuckets(16)
}
/**
* Gets all keys.
*
* @returns The keys.
*/
keys(): K[] {
const keys: K[] = []
for (const bucket of this.buckets) {
for (const entry of bucket) {
keys.push(entry.key)
}
}
return keys
}
/**
* Gets all values.
*
* @returns The values.
*/
values(): V[] {
const values: V[] = []
for (const bucket of this.buckets) {
for (const entry of bucket) {
values.push(entry.value)
}
}
return values
}
/**
* Gets all entries.
*
* @returns The entries.
*/
entries(): MapEntry<K, V>[] {
const entries: MapEntry<K, V>[] = []
for (const bucket of this.buckets) {
for (const entry of bucket) {
entries.push(entry)
}
}
return entries
}
/**
* Initializes the buckets.
*
* @param amount The amount of buckets to initialize.
*/
private initializeBuckets(amount: number): void {
this.buckets = []
for (let i = 0; i < amount; i++) {
this.buckets.push([])
}
}
/**
* Hashes a key to an index.
* This implementation uses the djb2 algorithm, which might not be the best.
* Feel free to change it to something else.
*
* @param key The key.
* @return The index.
*/
protected hash(key: K): number {
let hash = 0
for (let i = 0; i < String(key).length; i++) {
hash = (hash << 5) - hash + String(key).charCodeAt(i)
}
return hash % this.buckets.length
}
/**
* Resizes the hash map by doubling the amount of buckets.
*/
private resize(): void {
const entries = this.entries()
this.initializeBuckets(this.buckets.length * 2)
this.size = 0
for (const entry of entries) {
this.set(entry.key, entry.value)
}
}
}
/**
* Represents a key-value pair.
*
* @template K The type of the key.
* @template V The type of the value.
* @param key The key.
* @param value The value.
*/
export class MapEntry<K, V> {
key: K
value: V
constructor(key: K, value: V) {
this.key = key
this.value = value
}
}