-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhash_table.py
More file actions
127 lines (93 loc) · 2.88 KB
/
hash_table.py
File metadata and controls
127 lines (93 loc) · 2.88 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
from typing import List, Optional, NamedTuple, Any, Union
class Pair(NamedTuple):
key: Any
value: Any
class Deleted:
pass
class HashTable:
DELETED = Deleted()
def __init__(self, capacity: int = 4):
self.capacity = capacity
self._array: List[Optional[Union[Pair, Deleted]]] = [None] * self.capacity
@property
def capacity(self):
return self.__capacity
@capacity.setter
def capacity(self, value):
if value < 1:
raise ValueError("Capacity must be positive")
self.__capacity = value
@property
def array(self):
return {pair for pair in self._array if pair not in (None, self.DELETED)}
@property
def keys(self):
return {pair.key for pair in self.array}
@property
def values(self):
return {pair.value for pair in self.array}
def hash(self, key) -> int:
return hash(key) % self.capacity
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def _linear_probing(self, key):
idx = self.hash(key)
for _ in range(self.capacity):
yield idx, self._array[idx]
idx = (idx + 1) % self.capacity
def _resize(self):
copy = HashTable(capacity=self.capacity * 2)
for key, value in self.array:
copy[key] = value
self.capacity = copy.capacity
self._array = copy._array
def __setitem__(self, key, value):
for idx, pair in self._linear_probing(key):
if pair is self.DELETED:
continue
if pair is None or pair.key == key:
self._array[idx] = Pair(key, value)
break
else:
self._resize()
self[key] = value
def __getitem__(self, key):
for _, pair in self._linear_probing(key):
if pair is self.DELETED:
continue
if pair is None:
raise KeyError(key)
if pair.key == key:
return pair.value
else:
KeyError(key)
def __delitem__(self, key):
for idx, pair in self._linear_probing(key):
if pair is self.DELETED:
continue
if pair is None:
raise KeyError(key)
if pair.key == key:
self._array[idx] = self.DELETED
break
else:
raise KeyError(key)
def __contains__(self, key):
try:
self[key]
except KeyError:
raise False
else:
return True
def __iter__(self):
yield from self.keys
def __len__(self):
return len(self.array)
def __str__(self):
pairs = []
for key, value in self.array:
pairs.append(f"{key!r}: {value!r}")
return "{" + ", ".join(pairs) + "}"