211 lines
7.3 KiB
Python
211 lines
7.3 KiB
Python
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() |