2026-rff_mp/SorokinAD/[1]lab_1/MP_BST.py

253 lines
6.0 KiB
Python
Raw Normal View History

from MP_records import records
import random as rd
import time
import csv
import codecs
import sys
sys.setrecursionlimit(15000)
# ---------- Binary Search Tree ----------
# Узел:
# {
# "name": name,
# "phone": phone,
# "left": None,
# "right": None
# }
def bst_insert(root, name, phone):
"""
Вставляет новую запись или обновляет телефон по имени.
Возвращает корень дерева.
"""
if root is None:
return {
"name": name,
"phone": phone,
"left": None,
"right": None
}
if name == root["name"]:
root["phone"] = phone
elif name < root["name"]:
root["left"] = bst_insert(root["left"], name, phone)
else:
root["right"] = bst_insert(root["right"], name, phone)
return root
def bst_find(root, name):
"""
Поиск телефона по имени.
"""
if root is None:
return None
if name == root["name"]:
return root["phone"]
if name < root["name"]:
return bst_find(root["left"], name)
return bst_find(root["right"], name)
def bst_find_min(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"]
# Узел без правого потомка
if root["right"] is None:
return root["left"]
# Узел с двумя потомками
successor = bst_find_min(root["right"])
root["name"] = successor["name"]
root["phone"] = successor["phone"]
root["right"] = bst_delete(root["right"], successor["name"])
return root
def bst_inorder(root, result):
"""
Центрированный обход дерева.
"""
if root is None:
return
bst_inorder(root["left"], result)
result.append((root["name"], root["phone"]))
bst_inorder(root["right"], result)
def bst_list_all(root):
"""
Возвращает список записей в отсортированном порядке.
"""
result = []
bst_inorder(root, result)
return result
# ---------- Benchmark helpers ----------
def build_bst(records_list):
root = None
for name, phone in records_list:
root = bst_insert(root, name, phone)
return root
def measure_bst(records_list, mode_name, repeats=5):
rows = []
insertion_times = []
finding_times = []
deletion_times = []
for run_number in range(1, repeats + 1):
data = records_list[:]
if mode_name == "случайный":
rd.shuffle(data)
# А. Вставка
root = None
start = time.perf_counter()
for name, phone in data:
root = bst_insert(root, name, phone)
end = time.perf_counter()
insertion_time = end - start
insertion_times.append(insertion_time)
# Б. Поиск
existing_names = [name for name, phone in rd.sample(data, 100)]
missing_names = [f"None_{i}" for i in range(10)]
search_names = existing_names + missing_names
rd.shuffle(search_names)
start = time.perf_counter()
for name in search_names:
bst_find(root, name)
end = time.perf_counter()
finding_time = end - start
finding_times.append(finding_time)
# В. Удаление
delete_names = rd.sample(existing_names, 50)
start = time.perf_counter()
for name in delete_names:
root = bst_delete(root, name)
end = time.perf_counter()
deletion_time = end - start
deletion_times.append(deletion_time)
rows.append(["BinarySearchTree", mode_name, "вставка", run_number, insertion_time])
rows.append(["BinarySearchTree", mode_name, "поиск", run_number, finding_time])
rows.append(["BinarySearchTree", mode_name, "удаление", run_number, deletion_time])
rows.append(["BinarySearchTree", mode_name, "вставка", "среднее", sum(insertion_times) / repeats])
rows.append(["BinarySearchTree", mode_name, "поиск", "среднее", sum(finding_times) / repeats])
rows.append(["BinarySearchTree", mode_name, "удаление", "среднее", sum(deletion_times) / repeats])
return rows
def save_results(rows, filename="results.csv"):
with codecs.open(filename, "a+", "utf-16") as file:
writer = csv.writer(file)
writer.writerows(rows)
def run_shuffled(records_shuffled):
rows = measure_bst(records_shuffled, "случайный")
save_results(rows)
return rows
def run_sorted(records_sorted):
rows = measure_bst(records_sorted, "отсортированный")
save_results(rows)
return rows
# ---------- Manual tests ----------
def test():
root = None
root = bst_insert(root, "Ivan", "111")
root = bst_insert(root, "Anna", "222")
root = bst_insert(root, "Petr", "333")
root = bst_insert(root, "Maria", "444")
print(bst_find(root, "Anna")) # 222
print(bst_find(root, "Unknown")) # None
root = bst_insert(root, "Anna", "999")
print(bst_find(root, "Anna")) # 999
root = bst_delete(root, "Ivan")
print(bst_list_all(root))
if __name__ == "__main__":
records_shuffled, records_sorted = records()
run_shuffled(records_shuffled)
run_sorted(records_sorted)