-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhashTable.py
149 lines (111 loc) · 3.18 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
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
from types import SimpleNamespace
import numpy as np
class HashTable:
ratio_expand = .8
ratio_shrink = .2
min_size = 11
def __init__(self, size=None):
self._size = size or self.min_size
self._buckets = [None] * self._size
self._list = None
self._count = 0
def _entry(self, key):
# get hash and index
idx = hash(key) % self._size
# find entry by key
p, q = self._buckets[idx], None
while p and p.key != key:
p, q = p.next, p
# index, entry, previous entry
return idx, p, q
def _ensure_capacity(self):
fill = self._count / self._size
# expand or shrink?
if fill > self.ratio_expand:
self._size = self._size * 2 + 1
elif fill < self.ratio_shrink and self._size > self.min_size:
self._size = (self._size - 1) // 2
else:
return
# reallocate buckets
self._buckets = [None] * self._size
# store entries into new buckets
p = self._list
while p:
idx = hash(p.key) % self._size
p.next = self._buckets[idx]
self._buckets[idx] = p
p = p.entry_next
def __len__(self):
return self._count
def __contains__(self, key):
_, p, _ = self._entry(key)
return bool(p)
def __getitem__(self, key):
_, p, _ = self._entry(key)
return p and p.value
def __setitem__(self, key, value):
idx, p, _ = self._entry(key)
# set entry if key was found
if p:
p.value = value
return
# create new entry
p = SimpleNamespace(
key=key,
value=value,
next=self._buckets[idx],
entry_next=self._list,
entry_prev=None
)
# store to bucket
self._buckets[idx] = p
# store to list
if self._list:
self._list.entry_prev = p
self._list = p
# expand
self._count += 1
self._ensure_capacity()
def __delitem__(self, key):
idx, p, q = self._entry(key)
# key not found
if not p:
return
# remove from bucket
if q:
q.next = p.next
else:
self._buckets[idx] = p.next
# remove from list
if p.entry_next:
p.entry_next.entry_prev = p.entry_prev
if p.entry_prev:
p.entry_prev.entry_next = p.entry_next
else:
self._list = p.entry_next
# shrink
self._count -= 1
self._ensure_capacity()
def __iter__(self):
p = self._list
while p:
yield p.key
p = p.entry_next
def slots(self):
return ''.join(p and 'x' or '-' for p in self._buckets)
#RUN
table = HashTable()
# add random values
for _ in range(1000):
key, value = np.random.randint(1000), np.random.rand()
table[key] = value
len(table), table._size
table.slots()
# print some values
for key in list(table)[:5]:
print(key, table[key])
# delete all the values
for key in list(table):
del table[key]
len(table), table._size