diff --git a/src/bst.py b/src/bst.py new file mode 100644 index 0000000..04ba1d3 --- /dev/null +++ b/src/bst.py @@ -0,0 +1,88 @@ +# bst.py +# Двоичное дерево поиска по имени + +def create_node(name, phone): + """Создаёт узел дерева.""" + return { + 'name': name, + 'phone': phone, + 'left': None, + 'right': None + } + +def bst_insert(root, name, phone): + """ + Рекурсивно вставляет или обновляет запись. + Возвращает корень (может измениться при первой вставке). + """ + if root is None: + return create_node(name, phone) + + if name < root['name']: + root['left'] = bst_insert(root['left'], name, phone) + elif name > root['name']: + root['right'] = bst_insert(root['right'], name, phone) + else: # имя уже существует – обновляем телефон + root['phone'] = phone + return root + +def bst_find(root, name): + """Возвращает телефон или None.""" + if root is None: + return None + if name == root['name']: + return root['phone'] + elif name < root['name']: + return bst_find(root['left'], name) + else: + return bst_find(root['right'], name) + +def _min_node(node): + """Находит узел с минимальным именем в поддереве.""" + current = node + while current['left'] is not None: + current = current['left'] + return current + +def bst_delete(root, name): + """ + Удаляет узел с заданным именем. + Возвращает новый корень поддерева. + """ + if root is None: + return None + + if name < root['name']: + root['left'] = bst_delete(root['left'], name) + elif name > root['name']: + root['right'] = bst_delete(root['right'], name) + else: + # Узел найден + if root['left'] is None: + return root['right'] + elif root['right'] is None: + return root['left'] + + # Узел с двумя детьми: находим минимальный в правом поддереве + temp = _min_node(root['right']) + root['name'] = temp['name'] + root['phone'] = temp['phone'] + root['right'] = bst_delete(root['right'], temp['name']) + + return root + +def bst_list_all(root): + """ + Центрированный (in-order) обход – возвращает записи, + уже отсортированные по имени. + """ + def _inorder(node, result): + if node is None: + return + _inorder(node['left'], result) + result.append((node['name'], node['phone'])) + _inorder(node['right'], result) + + records = [] + _inorder(root, records) + return records \ No newline at end of file diff --git a/src/hash_table.py b/src/hash_table.py new file mode 100644 index 0000000..9f914f6 --- /dev/null +++ b/src/hash_table.py @@ -0,0 +1,46 @@ +# hash_table.py +# Хеш-таблица с цепочками (использует linked_list.py) + +import linked_list as ll + +def create_hash_table(size=1000): + """ + Создаёт пустую хеш-таблицу. + size – количество корзин (рекомендуется простое число). + """ + return [None] * size + +def _hash(name, table_size): + """Простая хеш-функция на основе суммы кодов символов.""" + return sum(ord(ch) for ch in name) % table_size + +def ht_insert(table, name, phone): + """Вставляет или обновляет запись.""" + idx = _hash(name, len(table)) + # Вставляем в связный список в этой корзине + table[idx] = ll.ll_insert(table[idx], name, phone) + +def ht_find(table, name): + """Ищет телефон по имени.""" + idx = _hash(name, len(table)) + return ll.ll_find(table[idx], name) + +def ht_delete(table, name): + """Удаляет запись по имени.""" + idx = _hash(name, len(table)) + table[idx] = ll.ll_delete(table[idx], name) + +def ht_list_all(table): + """ + Собирает все записи из всех корзин, + возвращает отсортированный по имени список. + """ + records = [] + for bucket in table: + # Каждая корзина – голова связного списка + current = bucket + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return record \ No newline at end of file diff --git a/src/linked_list.py b/src/linked_list.py new file mode 100644 index 0000000..fefab0b --- /dev/null +++ b/src/linked_list.py @@ -0,0 +1,74 @@ +# linked_list.py +# Связный список для телефонного справочника + +def create_node(name, phone): + """Создаёт новый узел-словарь.""" + return {'name': name, 'phone': phone, 'next': None} + +def ll_insert(head, name, phone): + """ + Вставляет или обновляет запись. + Если имя уже существует – обновляет телефон. + Если нет – добавляет в конец списка. + Возвращает голову списка (может измениться, если вставка в начало). + """ + # Если список пуст – создаём первый узел + if head is None: + return create_node(name, phone) + + # Проверяем, не находится ли имя в первом узле + if head['name'] == name: + head['phone'] = phone + return head + + # Ищем узел с таким именем или конец списка + current = head + while current['next'] is not None: + if current['next']['name'] == name: + current['next']['phone'] = phone + return head + current = current['next'] + + # Имя не найдено – добавляем в конец + current['next'] = create_node(name, phone) + return head + +def ll_find(head, name): + """Ищет телефон по имени. Возвращает phone или None.""" + current = head + while current is not None: + if current['name'] == name: + return current['phone'] + current = current['next'] + return None + +def ll_delete(head, name): + """Удаляет узел с заданным именем. Возвращает новую голову.""" + if head is None: + return None + + # Если удаляем голову + if head['name'] == name: + return head['next'] + + # Ищем предыдущий узел + current = head + while current['next'] is not None: + if current['next']['name'] == name: + current['next'] = current['next']['next'] + return head + current = current['next'] + return head + +def ll_list_all(head): + """ + Возвращает список всех записей в виде [(name, phone), ...], + отсортированный по имени. Сама структура не сортируется. + """ + records = [] + current = head + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) # сортировка по имени + return record \ No newline at end of file