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

253 lines
6.0 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.

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)