1
- # File: LinkedLists.rb
2
- #
3
- # Author: Sasha Goldenson
4
- #
5
- # License: Free to use
6
- #
7
- # Inspiration: Ron Tsui's (https://github.com/vokoshyv) excellent technical guidance has been very helpful in my understanding of these fundamentals.
8
- #
9
- # Motivation: Continuing my education, solidifying my fundamentals. Spreading the love.
10
- #
11
- # Hash Tables (AKA Hash Maps, Dictionaries, Hashes) are associative arrays of key-value storage. We're going to implement one using an array,
12
- # and build the hash table by populating that array with our key-value pairs.
13
- #
14
- # We'll need some properties and methods to describe and manipulate our Hash Table, namely:
15
- #
16
- # Properities: storage, buckets, size
17
- #
18
- # - Storage is our stored data, and will be represented by an array (of arrays, when populated.)
19
- #
20
- # - Buckets is the number of slots we have to store key-value pairs within storage. This number is variable, depending on storage contents. We'll
21
- # start our Hash Table by initializing it to 8.
22
- #
23
- # - Size is the number of key-value pairs we're currently storing in Storage.
24
- #
25
- # Methods: hash, insert, delete, retrieve, and resize
26
- #
27
- # - Hash is a method which takes the key to insert and the number of buckets, and evaluates it to the index which we'll insert into. There are many
28
- # different hashing methods, and we'll use the one provided here.
29
- #
30
- # - Insert(key, value), Delete(key), and Retrieve(key) methods are self-explanatory.
31
- #
32
- # - Resize is used when we need to increase the number of buckets our Hash Table has. If we wanted to double the number of buckets,
33
- # we would also have to reevaluate the data stored and assign them to their new buckets. The rule is when our size
34
- # (number of stored key-value pairs) exceeds 75% of the number of buckets, we'll double our number of buckets, and on the other side, we'll halve
35
- # the number of buckets when the number of stored key-value pairs drop below 25%.
36
- #
37
- # The handling of data collisions, when two different key-value to store hash to the same index, requires a separate chaining strategy.
38
- # Our separate chaining strategy is that we'll use an array in each location we store tuples, so we can store more than one key-value pairs which
39
- # hash to the same index.
40
- #
41
- ##########
1
+ ###############
2
+ # HashTable class
3
+ ###############
4
+
5
+ class HashTable
6
+
7
+ attr_accessor :storage , :buckets , :size , :resizing
8
+
9
+ def initialize
10
+ @storage = [ ]
11
+ @buckets = 8
12
+ @size = 0
13
+ @resizing = false
14
+ end
15
+
16
+ def hash ( str )
17
+ hash = 5381
18
+
19
+ str . split ( "" ) . each do |i |
20
+ char = i
21
+ hash = ( ( hash << 5 ) + hash ) + char . ord
22
+ end
23
+ return hash % self . buckets
24
+ end
25
+
26
+ def insert ( key , value )
27
+ bucket_index = self . hash ( key )
28
+ if self . storage [ bucket_index ] == nil
29
+ self . storage [ bucket_index ] = [ ]
30
+ self . storage [ bucket_index ] . push ( [ key , value ] )
31
+ self . size += 1
32
+ self . resize
33
+ else
34
+ bucket = self . storage [ bucket_index ]
35
+
36
+ bucket . length . times do |counter |
37
+ if bucket [ counter ] [ 0 ] == key
38
+ bucket [ counter ] [ 1 ] = value
39
+ end
40
+ end
41
+
42
+ bucket . push ( [ key , value ] )
43
+ self . size += 1
44
+ self . resize
45
+ end
46
+ end
47
+
48
+ def delete ( key )
49
+ bucket_index = self . hash ( key )
50
+
51
+ if self . storage [ bucket_index ] == nil
52
+ return "Key: '#{ key } ' not found."
53
+ else
54
+ bucket = self . storage [ bucket_index ]
55
+ bucket . length . times do |counter |
56
+ if bucket [ counter ] [ 0 ] == key
57
+ temp = bucket [ counter ] [ 1 ]
58
+ bucket . delete_at ( counter )
59
+ self . resize
60
+ self . size -= 1
61
+ return "key-value: [#{ key } ][#{ temp } ] was deleted."
62
+ end
63
+ end
64
+ return "This key was not found: '#{ key } '"
65
+ end
66
+ end
67
+
68
+ def retrieve ( key )
69
+ bucket_index = self . hash ( key )
70
+
71
+ if self . storage [ bucket_index ] == nil
72
+ return false
73
+ else
74
+ bucket = self . storage [ bucket_index ]
75
+ bucket . length . times do |counter |
76
+ if bucket [ counter ] [ 0 ] == key
77
+ return true
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def resize
84
+ allElements = [ ]
85
+ if self . size > ( 0.75 * self . buckets ) && ( self . resizing == false )
86
+ self . resizing = true
87
+ self . storage . each do |bucket |
88
+ if bucket != nil
89
+ bucket . each do |tuple |
90
+ allElements . push ( tuple )
91
+ end
92
+ end
93
+ end
94
+
95
+ self . storage = [ ]
96
+ self . size = 0
97
+ self . buckets = ( self . buckets * 2 )
98
+
99
+ allElements . each do |tuple |
100
+ self . insert ( tuple [ 0 ] , tuple [ 1 ] )
101
+ end
102
+ self . resizing = false
103
+ return 'HashTable number of buckets has been doubled.'
104
+ # We don't want our buckets to go below 8 buckets minimum
105
+ elsif ( self . size < ( 0.25 * self . buckets ) ) && self . buckets >= 9.0 && ( self . resizing == false )
106
+ self . resizing = true
107
+ self . storage . each do |bucket |
108
+ if bucket != nil
109
+ bucket . each do |tuple |
110
+ allElements . push ( tuple )
111
+ end
112
+ end
113
+ end
114
+
115
+ self . storage = [ ]
116
+ self . size = 0
117
+ self . buckets = ( self . buckets * 0.5 )
118
+
119
+ allElements . each do |tuple |
120
+ self . insert ( tuple [ 0 ] , tuple [ 1 ] )
121
+ end
122
+
123
+ self . resizing = false
124
+ return 'HashTable number of buckets have been halved.'
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ ############
42
131
# Unit Tests
43
- ##########
132
+ ############
44
133
45
134
require 'test/unit'
46
135
@@ -204,131 +293,3 @@ def test_hash_table_resize_should_halve_the_number_of_buckets_when_the_size_drop
204
293
assert_equal ( 8 , test . buckets )
205
294
end
206
295
end
207
- ###############
208
- # HashTable class
209
- ###############
210
-
211
- class HashTable
212
-
213
- attr_accessor :storage , :buckets , :size , :resizing
214
-
215
- def initialize
216
- @storage = [ ]
217
- @buckets = 8
218
- @size = 0
219
- @resizing = false
220
- end
221
-
222
- def hash ( str )
223
- hash = 5381
224
-
225
- str . split ( "" ) . each do |i |
226
- char = i
227
- hash = ( ( hash << 5 ) + hash ) + char . ord
228
- end
229
- return hash % self . buckets
230
- end
231
-
232
- def insert ( key , value )
233
- bucket_index = self . hash ( key )
234
- if self . storage [ bucket_index ] == nil
235
- self . storage [ bucket_index ] = [ ]
236
- self . storage [ bucket_index ] . push ( [ key , value ] )
237
- self . size += 1
238
- self . resize
239
- else
240
- bucket = self . storage [ bucket_index ]
241
-
242
- bucket . length . times do |counter |
243
- if bucket [ counter ] [ 0 ] == key
244
- bucket [ counter ] [ 1 ] = value
245
- end
246
- end
247
-
248
- bucket . push ( [ key , value ] )
249
- self . size += 1
250
- self . resize
251
- end
252
- end
253
-
254
- def delete ( key )
255
- bucket_index = self . hash ( key )
256
-
257
- if self . storage [ bucket_index ] == nil
258
- return "Key: '#{ key } ' not found."
259
- else
260
- bucket = self . storage [ bucket_index ]
261
- bucket . length . times do |counter |
262
- if bucket [ counter ] [ 0 ] == key
263
- temp = bucket [ counter ] [ 1 ]
264
- bucket . delete_at ( counter )
265
- self . resize
266
- self . size -= 1
267
- return "key-value: [#{ key } ][#{ temp } ] was deleted."
268
- end
269
- end
270
- return "This key was not found: '#{ key } '"
271
- end
272
- end
273
-
274
- def retrieve ( key )
275
- bucket_index = self . hash ( key )
276
-
277
- if self . storage [ bucket_index ] == nil
278
- return false
279
- else
280
- bucket = self . storage [ bucket_index ]
281
- bucket . length . times do |counter |
282
- if bucket [ counter ] [ 0 ] == key
283
- return true
284
- end
285
- end
286
- end
287
- end
288
-
289
- def resize
290
- allElements = [ ]
291
- if self . size > ( 0.75 * self . buckets ) && ( self . resizing == false )
292
- self . resizing = true
293
- self . storage . each do |bucket |
294
- if bucket != nil
295
- bucket . each do |tuple |
296
- allElements . push ( tuple )
297
- end
298
- end
299
- end
300
-
301
- self . storage = [ ]
302
- self . size = 0
303
- self . buckets = ( self . buckets * 2 )
304
-
305
- allElements . each do |tuple |
306
- self . insert ( tuple [ 0 ] , tuple [ 1 ] )
307
- end
308
- self . resizing = false
309
- return 'HashTable number of buckets has been doubled.'
310
- # We don't want our buckets to go below 8 buckets minimum
311
- elsif ( self . size < ( 0.25 * self . buckets ) ) && self . buckets >= 9.0 && ( self . resizing == false )
312
- self . resizing = true
313
- self . storage . each do |bucket |
314
- if bucket != nil
315
- bucket . each do |tuple |
316
- allElements . push ( tuple )
317
- end
318
- end
319
- end
320
-
321
- self . storage = [ ]
322
- self . size = 0
323
- self . buckets = ( self . buckets * 0.5 )
324
-
325
- allElements . each do |tuple |
326
- self . insert ( tuple [ 0 ] , tuple [ 1 ] )
327
- end
328
-
329
- self . resizing = false
330
- return 'HashTable number of buckets have been halved.'
331
- end
332
- end
333
-
334
- end
0 commit comments