2026-rff_mp/ShapovalovKA/docs/data/1Task/t1_3.py
2026-05-25 11:58:51 +03:00

211 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import random
import time
import csv
import os
# --------------------- Реализация бинарного дерева поиска (итеративная) ---------------------
def bst_insert(root, name, phone):
#Итеративная вставка. Возвращает корень.
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
if root is None:
return new_node
current = root
while True:
if name < current['name']:
if current['left'] is None:
current['left'] = new_node
break
else:
current = current['left']
elif name > current['name']:
if current['right'] is None:
current['right'] = new_node
break
else:
current = current['right']
else: # имя уже существует — обновляем телефон
current['phone'] = phone
break
return root
def bst_find(root, name):
#Итеративный поиск. Возвращает phone или None.
current = root
while current:
if name == current['name']:
return current['phone']
elif name < current['name']:
current = current['left']
else:
current = current['right']
return None
def bst_find_min(node):
#Возвращает узел с минимальным ключом в поддереве.
while node['left']:
node = node['left']
return node
def bst_delete(root, name):
#Итеративное удаление. Возвращает новый корень.
# Сначала найдём удаляемый узел и его родителя
parent = None
current = root
while current and current['name'] != name:
parent = current
if name < current['name']:
current = current['left']
else:
current = current['right']
if current is None: # узел не найден
return root
# Случай 1: нет левого потомка
if current['left'] is None:
child = current['right']
# Случай 2: нет правого потомка
elif current['right'] is None:
child = current['left']
# Случай 3: два потомка
else:
# Находим минимальный узел в правом поддереве (преемник)
min_parent = current
min_node = current['right']
while min_node['left']:
min_parent = min_node
min_node = min_node['left']
# Копируем данные из min_node в current
current['name'], current['phone'] = min_node['name'], min_node['phone']
# Удаляем min_node (у него нет левого потомка)
if min_parent['left'] == min_node:
min_parent['left'] = min_node['right']
else:
min_parent['right'] = min_node['right']
return root
# Подсоединяем child к parent
if parent is None:
return child
if parent['left'] == current:
parent['left'] = child
else:
parent['right'] = child
return root
def bst_list_all(root):
#Итеративный симметричный обход (inorder) без рекурсии, используя стек.
result = []
stack = []
current = root
while stack or current:
while current:
stack.append(current)
current = current['left']
current = stack.pop()
result.append((current['name'], current['phone']))
current = current['right']
return result
# --------------------- Функции измерений ---------------------
def generate_data(N=10000):
records = []
for i in range(N):
name = f"User_{i:05d}"
phone = f"8{random.randint(9000000000, 9999999999)}"
records.append((name, phone))
return records
def measure_insert(records):
root = None
start = time.perf_counter()
for name, phone in records:
root = bst_insert(root, name, phone)
end = time.perf_counter()
return end - start
def measure_find(records, test_names):
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
start = time.perf_counter()
for name in test_names:
bst_find(root, name)
end = time.perf_counter()
return end - start
def measure_delete(records, delete_names):
times = []
for name in delete_names:
root = None
for n, p in records:
root = bst_insert(root, n, p)
start = time.perf_counter()
root = bst_delete(root, name)
end = time.perf_counter()
times.append(end - start)
return sum(times) / len(times)
def main():
N = 10000
records = generate_data(N)
records_shuffled = records.copy()
random.shuffle(records_shuffled)
records_sorted = sorted(records, key=lambda x: x[0])
existing_names = random.sample([rec[0] for rec in records], 100)
non_existing = [f"None_{i}" for i in range(10)]
test_names = existing_names + non_existing
delete_names = random.sample([rec[0] for rec in records], 50)
insert_shuffled_avg = 0.0
insert_sorted_avg = 0.0
find_avg = 0.0
delete_avg = 0.0
repeats = 5
for _ in range(repeats):
insert_shuffled_avg += measure_insert(records_shuffled)
insert_sorted_avg += measure_insert(records_sorted)
find_avg += measure_find(records, test_names)
delete_avg += measure_delete(records, delete_names)
insert_shuffled_avg /= repeats
insert_sorted_avg /= repeats
find_avg /= repeats
delete_avg /= repeats
new_rows = [
["Binary tree", "случайный", "вставка (корень)", insert_shuffled_avg],
["Binary tree", "отсортированный", "вставка (корень)", insert_sorted_avg],
["Binary tree", "любой", "поиск 110 записей", find_avg],
["Binary tree", "любой", "удаление 50 записей (среднее)", delete_avg]
]
csv_filename = "results.csv"
file_exists = os.path.isfile(csv_filename)
need_header = False
if file_exists:
with open(csv_filename, 'r', encoding='utf-8-sig') as f:
first_line = f.readline()
if not first_line.startswith("Структура"):
need_header = True
else:
need_header = True
with open(csv_filename, 'a', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f, delimiter=';')
if need_header:
writer.writerow(["Структура", "Режим", "Операция", "Время (сек)"])
writer.writerows(new_rows)
print("Результаты для двоичного дерева поиска добавлены в", csv_filename)
print(f"Среднее время вставки (случ. порядок): {insert_shuffled_avg:.6f} сек")
print(f"Среднее время вставки (отсорт.): {insert_sorted_avg:.6f} сек")
print(f"Среднее время поиска 110 записей: {find_avg:.6f} сек")
print(f"Среднее время удаления 50 записей: {delete_avg:.6f} сек")
if __name__ == "__main__":
main()