-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathb_simplified.py
More file actions
185 lines (134 loc) · 6.99 KB
/
Copy pathb_simplified.py
File metadata and controls
185 lines (134 loc) · 6.99 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
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# https://contest.yandex.ru/contest/24414/run-report/155573748/
#
# -- Принцип работы --
#
# Класс `HashTable` реализует хеш-таблицу, в которой для разрешения коллизий применяется метод цепочек.
# В зависимости от значения хеш-функции для ключа, добавляемый в хеш-таблицу элемент помещается в одну
# из существующих корзин. Количество корзин фиксировано в течение всего периода жизни хеш-таблицы,
# поскольку рехеширование элементов не поддерживается.
#
# Для хранения элементов в корзине используются питоновский список. Если добавляемого элемента с
# некоторым значением ключа не существует, то он сохраняется в конец списка, а если элемент с таким
# ключом в списке уже есть, то происходит обновление его значения. При получении элемента по ключу и
# при его удалении выполняется цикл по элементам списка, пока нужный элемент не будет найден.
#
# -- Доказательство корректности --
#
# Поскольку при добавлении, получении и удалении элемента с некоторым значением ключа каждый раз
# выполняется обращение к одной и той же корзине хеш-таблицы, то добавленный элемент всегда может
# быть позднее найден по его ключу.
#
# -- Временная сложность --
#
# Временная сложность добавления элемента в хеш-таблицу соответствует временной сложности добавления
# элемента в конец списка корзины и в среднем составляет `O(1)`. Поскольку при получении по ключу и
# при удалении элемента список корзины просматривается в цикле, то временная сложность этих операций
# в среднем составляет `O(1 + α)`, где `α` — коэффициент заполнения хеш-таблицы.
#
# -- Пространственная сложность --
#
# Пространственная сложность хеш-таблицы составляет `O(n)`, где `n` — число элементов таблицы.
from __future__ import annotations
from collections.abc import Iterable, Iterator, Sequence, Callable
class BucketNode:
key: int
value: int
def __init__(self, key: int, value: int) -> None:
self.key = key
self.value = value
class BucketContainer:
nodes: list[BucketNode]
def __init__(self) -> None:
self.nodes = []
def __iter__(self) -> Iterator[BucketNode]:
yield from self.nodes
def find_node(self, key: int) -> BucketNode | None:
for bucket_node in self:
if bucket_node.key == key:
return bucket_node
return None
def create_node(self, key: int, value: int) -> BucketNode:
bucket_node = BucketNode(key, value)
self.nodes.append(bucket_node)
return bucket_node
def delete_node(self, bucket_node: BucketNode) -> None:
self.nodes.remove(bucket_node)
class Bucket:
container: BucketContainer
def __init__(self) -> None:
self.container = BucketContainer()
def get(self, key: int) -> int | None:
bucket_node = self.container.find_node(key)
if bucket_node is None:
return None
return bucket_node.value
def put(self, key: int, value: int) -> None:
bucket_node = self.container.find_node(key)
if bucket_node is None:
self.container.create_node(key, value)
return
bucket_node.value = value
def delete(self, key: int) -> int | None:
bucket_node = self.container.find_node(key)
if bucket_node is None:
return None
value = bucket_node.value
self.container.delete_node(bucket_node)
return value
class HashTable:
capacity: int
buckets: Sequence[Bucket]
def __init__(self, *, capacity: int) -> None:
self.capacity = capacity
self.buckets = [Bucket() for _i in range(capacity)]
def _get_bucket(self, key: int) -> Bucket:
return self.buckets[hash(key) % self.capacity]
def get(self, key: int) -> int | None:
bucket = self._get_bucket(key)
return bucket.get(key)
def put(self, key: int, value: int) -> None:
bucket = self._get_bucket(key)
bucket.put(key, value)
def delete(self, key: int) -> int | None:
bucket = self._get_bucket(key)
return bucket.delete(key)
type HashTableCommandArgs = Sequence[int]
type HashTableCommandParser = Callable[[HashTableCommandArgs], str | None]
class HashTableCommandsExecutor:
hash_table: HashTable
command_parsers: Iterable[tuple[str, HashTableCommandParser]]
def __init__(self, hash_table: HashTable) -> None:
self.hash_table = hash_table
self.command_parsers = self.get_command_parsers()
def get_command_parsers(self) -> Iterable[tuple[str, HashTableCommandParser]]:
return [
('get', self._parse_get),
('put', self._parse_put),
('delete', self._parse_delete),
]
def execute(self, command_str: str) -> str | None:
for command_name, command_parser in self.command_parsers:
command_prefix = command_name + ' '
if command_str.startswith(command_prefix):
command_args = list(map(int, command_str.removeprefix(command_prefix).split()))
return command_parser(command_args)
return None
def _parse_get(self, command_args: HashTableCommandArgs) -> str:
value = self.hash_table.get(command_args[0])
return str(value)
def _parse_put(self, command_args: HashTableCommandArgs) -> None:
self.hash_table.put(command_args[0], command_args[1])
def _parse_delete(self, command_args: HashTableCommandArgs) -> str:
value = self.hash_table.delete(command_args[0])
return str(value)
def main() -> None:
commands_count = int(input().strip())
hash_table = HashTable(capacity=10000)
commands_executor = HashTableCommandsExecutor(hash_table)
for i in range(commands_count):
command_str = input().strip()
result = commands_executor.execute(command_str)
if result is not None:
print(result)
if __name__ == '__main__':
main()