-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathdouble_key_table.py
167 lines (133 loc) · 4.87 KB
/
double_key_table.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
from __future__ import annotations
from typing import Generic, TypeVar, Iterator
from data_structures.hash_table import LinearProbeTable, FullError
from data_structures.referential_array import ArrayR
K1 = TypeVar('K1')
K2 = TypeVar('K2')
V = TypeVar('V')
class DoubleKeyTable(Generic[K1, K2, V]):
"""
Double Hash Table.
Type Arguments:
- K1: 1st Key Type. In most cases should be string.
Otherwise `hash1` should be overwritten.
- K2: 2nd Key Type. In most cases should be string.
Otherwise `hash2` should be overwritten.
- V: Value Type.
Unless stated otherwise, all methods have O(1) complexity.
"""
# No test case should exceed 1 million entries.
TABLE_SIZES = [5, 13, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869]
HASH_BASE = 31
def __init__(self, sizes:list|None=None, internal_sizes:list|None=None) -> None:
raise NotImplementedError()
def hash1(self, key: K1) -> int:
"""
Hash the 1st key for insert/retrieve/update into the hashtable.
:complexity: O(len(key))
"""
value = 0
a = 31415
for char in key:
value = (ord(char) + a * value) % self.table_size
a = a * self.HASH_BASE % (self.table_size - 1)
return value
def hash2(self, key: K2, sub_table: LinearProbeTable[K2, V]) -> int:
"""
Hash the 2nd key for insert/retrieve/update into the hashtable.
:complexity: O(len(key))
"""
value = 0
a = 31415
for char in key:
value = (ord(char) + a * value) % sub_table.table_size
a = a * self.HASH_BASE % (sub_table.table_size - 1)
return value
def _linear_probe(self, key1: K1, key2: K2, is_insert: bool) -> tuple[int, int]:
"""
Find the correct position for this key in the hash table using linear probing.
:raises KeyError: When the key pair is not in the table, but is_insert is False.
:raises FullError: When a table is full and cannot be inserted.
"""
raise NotImplementedError()
def iter_keys(self, key:K1|None=None) -> Iterator[K1|K2]:
"""
key = None:
Returns an iterator of all top-level keys in hash table
key = k:
Returns an iterator of all keys in the bottom-hash-table for k.
"""
raise NotImplementedError()
def keys(self, key:K1|None=None) -> list[K1|K2]:
"""
key = None: returns all top-level keys in the table.
key = x: returns all bottom-level keys for top-level key x.
"""
raise NotImplementedError()
def iter_values(self, key:K1|None=None) -> Iterator[V]:
"""
key = None:
Returns an iterator of all values in hash table
key = k:
Returns an iterator of all values in the bottom-hash-table for k.
"""
raise NotImplementedError()
def values(self, key:K1|None=None) -> list[V]:
"""
key = None: returns all values in the table.
key = x: returns all values for top-level key x.
"""
raise NotImplementedError()
def __contains__(self, key: tuple[K1, K2]) -> bool:
"""
Checks to see if the given key is in the Hash Table
:complexity: See linear probe.
"""
try:
_ = self[key]
except KeyError:
return False
else:
return True
def __getitem__(self, key: tuple[K1, K2]) -> V:
"""
Get the value at a certain key
:raises KeyError: when the key doesn't exist.
"""
raise NotImplementedError()
def __setitem__(self, key: tuple[K1, K2], data: V) -> None:
"""
Set an (key, value) pair in our hash table.
"""
raise NotImplementedError()
def __delitem__(self, key: tuple[K1, K2]) -> None:
"""
Deletes a (key, value) pair in our hash table.
:raises KeyError: when the key doesn't exist.
"""
raise NotImplementedError()
def _rehash(self) -> None:
"""
Need to resize table and reinsert all values
:complexity best: O(N*hash(K)) No probing.
:complexity worst: O(N*hash(K) + N^2*comp(K)) Lots of probing.
Where N is len(self)
"""
raise NotImplementedError()
@property
def table_size(self) -> int:
"""
Return the current size of the table (different from the length)
"""
raise NotImplementedError()
def __len__(self) -> int:
"""
Returns number of elements in the hash table
"""
raise NotImplementedError()
def __str__(self) -> str:
"""
String representation.
Not required but may be a good testing tool.
"""
raise NotImplementedError()