forked from keon/algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hashtable.py
98 lines (80 loc) · 2.98 KB
/
hashtable.py
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
"""
MAP Abstract Data Type
Map() Create a new, empty map. It returns an empty map collection.
put(key,val) Add a new key-value pair to the map. If the key is already in the map then replace the old value with the new value.
get(key) Given a key, return the value stored in the map or None otherwise.
del Delete the key-value pair from the map using a statement of the form del map[key].
len() Return the number of key-value pairs stored in the map.
in Return True for a statement of the form key in map, if the given key is in the map, False otherwise.
"""
from unittest import TestCase
class HashTable(object):
_empty = object()
_deleted = object()
def __init__(self, size=11):
self.size = size
self._keys = [self._empty] * size # keys
self._values = [self._empty] * size # values
def put(self, key, value):
hash_ = self.hash(key)
while True:
if self._keys[hash_] is self._empty or self._keys[hash_] is self._deleted:
# can assign to hash_ index
self._keys[hash_] = key
self._values[hash_] = value
return
elif self._keys[hash_] == key:
# key already exists here, assign over
self._keys[hash_] = key
self._values[hash_] = value
return
hash_ = self.rehash(hash_)
def get(self, key):
initial_hash = hash_ = self.hash(key)
while True:
if self._keys[hash_] is self._empty:
# That key was never assigned
return None
elif self._keys[hash_] == key:
# key found
return self._values[hash_]
hash_ = self.rehash(hash_)
if initial_hash == hash_:
# table is full and wrapped around
return None
def hash(self, key):
return key % self.size
def rehash(self, old_hash):
"""
linear probing
"""
return (old_hash + 1) % self.size
def __getitem__(self, key):
return self.get(key)
def __setitem__(self, key, value):
self.put(key, value)
class TestHashTable(TestCase):
def test_one_entry(self):
m = HashTable(10)
m.put(1, '1')
self.assertEqual('1', m.get(1))
def test_add_entry_bigger_than_table_size(self):
m = HashTable(10)
m.put(11, '1')
self.assertEqual('1', m.get(11))
def test_get_none_if_key_missing_and_hash_collision(self):
m = HashTable(10)
m.put(1, '1')
self.assertEqual(None, m.get(11))
def test_two_entries_with_same_hash(self):
m = HashTable(10)
m.put(1, '1')
m.put(11, '11')
self.assertEqual('1', m.get(1))
self.assertEqual('11', m.get(11))
def test_get_on_full_table_does_halts(self):
# and does not search forever
m = HashTable(10)
for i in range(10, 20):
m.put(i, i)
self.assertEqual(None, m.get(1))