-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhashmap.py
113 lines (96 loc) · 3.46 KB
/
hashmap.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
#
# https://wiki.python.org/moin/TimeComplexity
#
# Common Collision Policies:
# [x] Separate bucketing
# [ ] Open Addressing: store in the next available empty slot in the array
# can be implemented using linear probing or quadratic probing or [double hash]
# [ ] Robinhood Hashing: add to the index, reorganise the bucket for ideal index
#
from typing import Iterator
Input = int | float | str | tuple
class HashMap:
def __init__(self, capacity: int = 10) -> None:
"""To minimise memory needed for the list on initialisation
a capacity is used to allocate bucket for hash keys.
time complexity: O(1)
"""
self.capacity = capacity
self.buckets = [[] for _ in range(self.capacity)]
self.length = 0
def _hash_key(self, key: Input) -> int:
"""Time complexity: O(1)."""
return hash(key) % self.capacity
def __len__(self) -> int:
"""Time complexity: O(1)."""
return self.length
def __contains__(self, key: Input) -> bool:
"""Average time complexity: O(1), assuming collisions are uncommon
amortised time complexity: O(N), in case of collisions.
"""
index = self._hash_key(key)
for k, _ in self.buckets[index]:
if k == key:
return True
return False
def __iter__(self) -> Iterator:
"""Time complexity: O(N)."""
for bucket in self.buckets:
for key, _ in bucket:
yield key
def __getitem__(self, key: Input) -> any:
"""Average time complexity: O(1), assuming collisions are uncommon
amortised time complexity: O(N), in case of collisions.
"""
index = self._hash_key(key)
for k, v in self.buckets[index]:
if k == key:
return v
raise KeyError(f"{key} not found")
def __setitem__(self, key: Input, value: any = None) -> None:
"""Average time complexity: O(1), assuming collisions are uncommon
amortised time complexity: O(N), in case of collisions.
"""
index = self._hash_key(key)
for entry in self.buckets[index]:
if entry[0] == key:
entry[1] = value
return
self.buckets[index].append([key, value])
self.length += 1
def __delitem__(self, key: Input) -> None:
"""Average time complexity: O(1), assuming collisions are uncommon
amortised time complexity: O(N), in case of collisions.
"""
index = self._hash_key(key)
for i, (k, _) in enumerate(self.buckets[index]):
if k == key:
del self.buckets[index][i]
self.length -= 1
return
raise KeyError(f"{key} not found")
contains = __contains__
get = __getitem__
put = __setitem__
remove = __delitem__
def keys(self) -> list[Input]:
"""Time complexity: O(N)."""
keys = []
for bucket in self.buckets:
for k, _ in bucket:
keys.append(k)
return keys
def values(self) -> list[any]:
"""Time complexity: O(N)."""
values = []
for bucket in self.buckets:
for _, v in bucket:
values.append(v)
return values
def items(self) -> list[tuple[Input, any]]:
"""Time complexity: O(N)."""
items = []
for bucket in self.buckets:
for pair in bucket:
items.append(tuple(pair))
return items