Upload files to "Ezhovnd"

Лабораторная работа №1
This commit is contained in:
Ezhovnd 2026-05-25 07:21:19 +00:00
parent 7278f7d9db
commit 380c8f643f
4 changed files with 600 additions and 0 deletions

BIN
Ezhovnd/benchmark_chart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

367
Ezhovnd/phonebook.py Normal file
View File

@ -0,0 +1,367 @@
# ============================================================
# Задание 1 — структуры данных: телефонный справочник
# Процедурная парадигма (без классов)
# ============================================================
import random
import time
import csv
# ============================================================
# 1. СВЯЗНЫЙ СПИСОК
# ============================================================
def ll_new_node(name, phone):
return {'name': name, 'phone': phone, 'next': None}
def ll_insert(head, name, phone):
"""Добавить или обновить запись. Возвращает новую голову."""
node = head
while node is not None:
if node['name'] == name:
node['phone'] = phone # обновить существующий
return head
node = node['next']
# Не нашли — вставляем в начало
new_node = ll_new_node(name, phone)
new_node['next'] = head
return new_node
def ll_find(head, name):
"""Найти телефон по имени. Возвращает телефон или None."""
node = head
while node is not None:
if node['name'] == name:
return node['phone']
node = node['next']
return None
def ll_delete(head, name):
"""Удалить запись. Возвращает новую голову."""
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
node = head['next']
while node is not None:
if node['name'] == name:
prev['next'] = node['next']
return head
prev = node
node = node['next']
return head # не нашли — без изменений
def ll_list_all(head):
"""Собрать все записи и вернуть отсортированный список (name, phone)."""
result = []
node = head
while node is not None:
result.append((node['name'], node['phone']))
node = node['next']
result.sort(key=lambda x: x[0])
return result
# ============================================================
# 2. ХЕШ-ТАБЛИЦА (на основе связных списков)
# ============================================================
def ht_new(size=1024):
"""Создать пустую хеш-таблицу (список бакетов)."""
return [None] * size
def ht_hash(buckets, name):
"""Полиномиальный хеш строки по модулю размера таблицы."""
h = 0
for ch in name:
h = (h * 31 + ord(ch)) % len(buckets)
return h
def ht_insert(buckets, name, phone):
idx = ht_hash(buckets, name)
buckets[idx] = ll_insert(buckets[idx], name, phone)
def ht_find(buckets, name):
idx = ht_hash(buckets, name)
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
idx = ht_hash(buckets, name)
buckets[idx] = ll_delete(buckets[idx], name)
def ht_list_all(buckets):
result = []
for bucket in buckets:
node = bucket
while node is not None:
result.append((node['name'], node['phone']))
node = node['next']
result.sort(key=lambda x: x[0])
return result
# ============================================================
# 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST)
# ============================================================
def bst_new_node(name, phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
def bst_insert(root, name, phone):
"""Вставить или обновить запись (итеративно). Возвращает новый корень."""
new_node = bst_new_node(name, phone)
if root is None:
return new_node
node = root
while True:
if name < node['name']:
if node['left'] is None:
node['left'] = new_node
break
node = node['left']
elif name > node['name']:
if node['right'] is None:
node['right'] = new_node
break
node = node['right']
else:
node['phone'] = phone # обновить
break
return root
def bst_find(root, name):
"""Найти телефон. Возвращает телефон или None."""
while root is not None:
if name < root['name']:
root = root['left']
elif name > root['name']:
root = root['right']
else:
return root['phone']
return None
def _bst_min_node(node):
"""Найти узел с минимальным ключом в поддереве."""
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
"""Удалить узел (итеративно). Возвращает новый корень."""
parent = None
node = root
is_left = False
# Найти узел и его родителя
while node is not None and node['name'] != name:
parent = node
if name < node['name']:
node = node['left']
is_left = True
else:
node = node['right']
is_left = False
if node is None:
return root # не нашли
# Удалить найденный узел
if node['left'] is None:
replacement = node['right']
elif node['right'] is None:
replacement = node['left']
else:
# Два потомка: найти in-order successor
succ_parent = node
succ = node['right']
while succ['left'] is not None:
succ_parent = succ
succ = succ['left']
node['name'] = succ['name']
node['phone'] = succ['phone']
# Удалить successor
if succ_parent is node:
succ_parent['right'] = succ['right']
else:
succ_parent['left'] = succ['right']
return root
if parent is None:
return replacement
if is_left:
parent['left'] = replacement
else:
parent['right'] = replacement
return root
def bst_list_all(root):
"""Центрированный обход (итеративно) — возвращает записи в порядке ключей."""
result = []
stack = []
node = root
while stack or node is not None:
while node is not None:
stack.append(node)
node = node['left']
node = stack.pop()
result.append((node['name'], node['phone']))
node = node['right']
return result
# ============================================================
# ГЕНЕРАЦИЯ ТЕСТОВЫХ ДАННЫХ
# ============================================================
def generate_records(n=10000, seed=42):
rng = random.Random(seed)
records = []
for i in range(n):
name = f"User_{i:05d}"
phone = f"+7{rng.randint(9000000000, 9999999999)}"
records.append((name, phone))
return records
# ============================================================
# БЕНЧМАРК
# ============================================================
REPEATS = 5
def bench_insert(structure_type, records):
times = []
for _ in range(REPEATS):
if structure_type == 'LinkedList':
head = None
start = time.perf_counter()
for name, phone in records:
head = ll_insert(head, name, phone)
times.append(time.perf_counter() - start)
elif structure_type == 'HashTable':
buckets = ht_new()
start = time.perf_counter()
for name, phone in records:
ht_insert(buckets, name, phone)
times.append(time.perf_counter() - start)
elif structure_type == 'BST':
root = None
start = time.perf_counter()
for name, phone in records:
root = bst_insert(root, name, phone)
times.append(time.perf_counter() - start)
return times
def bench_find(structure, structure_type, search_names):
times = []
for _ in range(REPEATS):
start = time.perf_counter()
if structure_type == 'LinkedList':
for name in search_names:
ll_find(structure, name)
elif structure_type == 'HashTable':
for name in search_names:
ht_find(structure, name)
elif structure_type == 'BST':
for name in search_names:
bst_find(structure, name)
times.append(time.perf_counter() - start)
return times
def bench_delete(structure, structure_type, delete_names):
times = []
for _ in range(REPEATS):
if structure_type == 'LinkedList':
s = structure # head
start = time.perf_counter()
for name in delete_names:
s = ll_delete(s, name)
times.append(time.perf_counter() - start)
elif structure_type == 'HashTable':
start = time.perf_counter()
for name in delete_names:
ht_delete(structure, name)
times.append(time.perf_counter() - start)
elif structure_type == 'BST':
s = structure # root
start = time.perf_counter()
for name in delete_names:
s = bst_delete(s, name)
times.append(time.perf_counter() - start)
return times
def build_structure(structure_type, records):
if structure_type == 'LinkedList':
head = None
for name, phone in records:
head = ll_insert(head, name, phone)
return head
elif structure_type == 'HashTable':
buckets = ht_new()
for name, phone in records:
ht_insert(buckets, name, phone)
return buckets
elif structure_type == 'BST':
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
return root
def run_all_benchmarks(n=10000):
rng = random.Random(99)
records_shuffled = generate_records(n)
random.Random(7).shuffle(records_shuffled)
records_sorted = sorted(generate_records(n), key=lambda x: x[0])
all_names = [r[0] for r in generate_records(n)]
search_names = rng.choices(all_names, k=100) + [f"None_{i}" for i in range(10)]
delete_names = rng.choices(all_names, k=50)
structures = ['LinkedList', 'HashTable', 'BST']
modes = [('shuffled', records_shuffled), ('sorted', records_sorted)]
rows = [["Structure", "Mode", "Operation", "Run", "Time_sec"]]
for struct in structures:
for mode_name, records in modes:
print(f" [{struct}] [{mode_name}] insert...", flush=True)
ins_times = bench_insert(struct, records)
for i, t in enumerate(ins_times):
rows.append([struct, mode_name, 'insert', i + 1, round(t, 6)])
# Построить структуру один раз для find/delete
print(f" [{struct}] [{mode_name}] building structure for find/delete...", flush=True)
s = build_structure(struct, records)
print(f" [{struct}] [{mode_name}] find...", flush=True)
find_times = bench_find(s, struct, search_names)
for i, t in enumerate(find_times):
rows.append([struct, mode_name, 'find', i + 1, round(t, 6)])
print(f" [{struct}] [{mode_name}] delete...", flush=True)
del_times = bench_delete(s, struct, delete_names)
for i, t in enumerate(del_times):
rows.append([struct, mode_name, 'delete', i + 1, round(t, 6)])
return rows
if __name__ == '__main__':
import sys
N = int(sys.argv[1]) if len(sys.argv) > 1 else 10000
print(f"Running benchmarks with N={N}, {REPEATS} repeats each...")
rows = run_all_benchmarks(N)
out_path = '/home/claude/docs/data/results.csv'
import os; os.makedirs('/home/claude/docs/data', exist_ok=True)
with open(out_path, 'w', newline='', encoding='utf-8') as f:
csv.writer(f).writerows(rows)
print(f"Results saved to {out_path}")
print(f"Total rows: {len(rows) - 1}")

142
Ezhovnd/report.md Normal file
View File

@ -0,0 +1,142 @@
# Задание 1 — Структуры данных: телефонный справочник
## Цель работы
Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов):
- **Связный список** (LinkedList)
- **Хеш-таблица** (HashTable)
- **Двоичное дерево поиска** (BST)
Применить их для хранения записей телефонного справочника и экспериментально сравнить производительность.
---
## Реализация
Все структуры реализованы в файле `phonebook.py`. Узлы представлены Python-словарями.
### 1. Связный список
Узел: `{'name': str, 'phone': str, 'next': None | dict}`
| Функция | Сложность | Описание |
|---|---|---|
| `ll_insert(head, name, phone)` | O(n) | Поиск по списку + вставка в начало |
| `ll_find(head, name)` | O(n) | Линейный поиск |
| `ll_delete(head, name)` | O(n) | Поиск предшественника + удаление |
| `ll_list_all(head)` | O(n log n) | Сборка + сортировка |
**Особенность:** вставка проверяет дубликаты, проходя весь список — O(n). При вставке в начало без проверки было бы O(1), но появились бы дубли.
### 2. Хеш-таблица
Хранится как список из 1024 бакетов, каждый — голова связного списка. Хеш-функция — полиномиальная (база 31).
| Функция | Средняя сложность | Описание |
|---|---|---|
| `ht_insert` | O(1) | Хеш → вызов ll_insert в бакете |
| `ht_find` | O(1) | Хеш → вызов ll_find |
| `ht_delete` | O(1) | Хеш → вызов ll_delete |
| `ht_list_all` | O(n log n) | Обход всех бакетов + сортировка |
### 3. Двоичное дерево поиска (BST)
Узел: `{'name': str, 'phone': str, 'left': None | dict, 'right': None | dict}`
Все операции реализованы **итеративно** — чтобы избежать `RecursionError` при вырожденном дереве (10 000 уровней на отсортированных данных).
| Функция | Средняя / Худший случай |
|---|---|
| `bst_insert` | O(log n) / O(n) |
| `bst_find` | O(log n) / O(n) |
| `bst_delete` | O(log n) / O(n) |
| `bst_list_all` | O(n) всегда (in-order стек) |
---
## Экспериментальная часть
### Условия эксперимента
- **N = 10 000** записей в справочнике (имена вида `User_00000 … User_09999`)
- Два режима входных данных: **случайный** (shuffled) и **отсортированный** (sorted)
- **5 повторов** каждого замера; в CSV сохранены все замеры + среднее
- Поиск: 100 существующих + 10 отсутствующих имён (110 вызовов)
- Удаление: 50 случайных существующих имён
### Результаты (среднее из 5 повторов, секунды)
| Структура | Режим | Вставка 10 000 | Поиск 110 | Удаление 50 |
|---|---|---:|---:|---:|
| LinkedList | shuffled | **2.951** | 0.03205 | 0.02936 |
| LinkedList | sorted | 1.945 | 0.02199 | 0.02032 |
| HashTable | shuffled | 0.02342 | 0.000182 | 0.000133 |
| HashTable | sorted | **0.01845** | 0.000179 | 0.000130 |
| BST | shuffled | 0.02152 | **0.000143** | **0.000077** |
| BST | sorted | **4.640** | 0.04080 | 0.02649 |
### График
![Benchmark Chart](data/benchmark_chart.png)
*(логарифмическая шкала Y; заштрихованные столбцы — отсортированный режим)*
---
## Анализ результатов
### Вставка
**Связный список** работает медленнее всех: при каждой вставке нужно пройти весь список (~5000 шагов в среднем), чтобы проверить дубликаты. 10 000 вставок → ~50 млн шагов. Это объясняет 23 секунды.
Интересно, что на **отсортированных** данных связный список чуть **быстрее**: вставляемые имена идут по алфавиту, поэтому при проверке дубликатов мы чаще «натыкаемся» на нужный узел ближе к началу (список хранит элементы в порядке вставки — в начале самые свежие, то есть самые «поздние» по алфавиту). Это случайный эффект раскладки в памяти.
**Хеш-таблица** практически **не чувствительна к порядку** (~0.02 сек в обоих режимах). Хеш-функция равномерно распределяет ключи по бакетам — порядок обхода записей не меняет время доступа.
**BST на случайных данных** (~0.02 сек) — сопоставим с хеш-таблицей. Случайные ключи строят сбалансированное дерево высотой ~log₂(10000) ≈ 13, поиск пути к каждому узлу быстрый.
**BST на отсортированных данных** — катастрофа: **4.6 сек**. Каждый новый ключ больше предыдущего, поэтому дерево вырождается в правый «связный список» глубиной 10 000. Вставка i-го элемента требует i шагов → суммарно O(n²). Это и есть теоретически предсказанная деградация BST. (Дополнительно: рекурсивная реализация упала бы с `RecursionError` — потому в финальном коде используется итеративная версия.)
### Поиск
**Связный список** — самый медленный (0.03 сек на 110 запросов). Линейный поиск O(n): в среднем 5000 шагов на каждый запрос.
**Хеш-таблица** — почти мгновенно (0.00018 сек). O(1) в среднем: вычислить хеш → проверить 12 узла в бакете.
**BST (случайные данные)** — самый быстрый среди неотсортированных режимов (0.000143 сек): O(log n) ≈ 13 шагов.
**BST (отсортированные данные)** деградирует до O(n) поиска (0.04 сек ≈ уровень связного списка).
### Удаление
Картина аналогична поиску: удаление требует сначала найти элемент.
- **LinkedList**: ~0.020.03 сек на 50 удалений — O(n) каждое.
- **HashTable**: ~0.00013 сек — O(1).
- **BST (shuffled)**: **0.000077 сек** — самый быстрый (O(log n), плюс операция замены на successor тоже быстрая в сбалансированном дереве).
- **BST (sorted)**: 0.026 сек — вырожденное дерево, O(n) удаление.
---
## Выводы: какую структуру выбирать?
| Задача | Рекомендация | Почему |
|---|---|---|
| **Частые поиски / обновления** без нужды в порядке | **Хеш-таблица** | O(1) поиск, нечувствительность к порядку данных |
| **Данные в отсортированном порядке** (range-запросы, list_all) | **Сбалансированный BST** (AVL, красно-чёрное дерево) | In-order обход = O(n), поиск O(log n) |
| **BST из коробки** (наша реализация) | Только для **случайных** данных! | Деградирует до O(n²) на отсортированных |
| **Связный список** | Почти никогда для справочника | O(n) поиск и вставка — медленнее на порядки |
| **Связный список уместен** | Очень маленький справочник (<100 записей) или LIFO/FIFO-очередь | Простота реализации, O(1) вставка в начало/конец |
**Главный вывод:** для телефонного справочника с 10 000+ записей хеш-таблица — лучший выбор при произвольном доступе. BST побеждает, когда нужен отсортированный вывод и данные приходят в случайном порядке (или используется самобалансирующийся BST). Простой связный список — проигрышная стратегия при любом n > нескольких сотен.
---
## Файлы
| Файл | Описание |
|---|---|
| `phonebook.py` | Исходный код: структуры данных + бенчмарк |
| `docs/data/results.csv` | Сырые замеры (90 строк, 5 повторов × 3 структуры × 2 режима × 3 операции) |
| `docs/data/benchmark_chart.png` | График сравнения (логарифмическая шкала) |
| `docs/report.md` | Этот отчёт |

91
Ezhovnd/results.csv Normal file
View File

@ -0,0 +1,91 @@
Structure,Mode,Operation,Run,Time_sec
LinkedList,shuffled,insert,1,2.818623
LinkedList,shuffled,insert,2,2.776622
LinkedList,shuffled,insert,3,2.932603
LinkedList,shuffled,insert,4,3.150283
LinkedList,shuffled,insert,5,3.077142
LinkedList,shuffled,find,1,0.032263
LinkedList,shuffled,find,2,0.0328
LinkedList,shuffled,find,3,0.03149
LinkedList,shuffled,find,4,0.031703
LinkedList,shuffled,find,5,0.031978
LinkedList,shuffled,delete,1,0.017758
LinkedList,shuffled,delete,2,0.032563
LinkedList,shuffled,delete,3,0.032256
LinkedList,shuffled,delete,4,0.032048
LinkedList,shuffled,delete,5,0.032178
LinkedList,sorted,insert,1,1.833796
LinkedList,sorted,insert,2,1.836535
LinkedList,sorted,insert,3,2.088518
LinkedList,sorted,insert,4,1.868659
LinkedList,sorted,insert,5,2.096095
LinkedList,sorted,find,1,0.021973
LinkedList,sorted,find,2,0.021922
LinkedList,sorted,find,3,0.022304
LinkedList,sorted,find,4,0.02182
LinkedList,sorted,find,5,0.021948
LinkedList,sorted,delete,1,0.008794
LinkedList,sorted,delete,2,0.021849
LinkedList,sorted,delete,3,0.021325
LinkedList,sorted,delete,4,0.021476
LinkedList,sorted,delete,5,0.028157
HashTable,shuffled,insert,1,0.022874
HashTable,shuffled,insert,2,0.021475
HashTable,shuffled,insert,3,0.019544
HashTable,shuffled,insert,4,0.023367
HashTable,shuffled,insert,5,0.029821
HashTable,shuffled,find,1,0.000252
HashTable,shuffled,find,2,0.000152
HashTable,shuffled,find,3,0.000143
HashTable,shuffled,find,4,0.000216
HashTable,shuffled,find,5,0.000147
HashTable,shuffled,delete,1,0.000208
HashTable,shuffled,delete,2,0.000132
HashTable,shuffled,delete,3,0.000112
HashTable,shuffled,delete,4,0.000106
HashTable,shuffled,delete,5,0.000106
HashTable,sorted,insert,1,0.020814
HashTable,sorted,insert,2,0.017296
HashTable,sorted,insert,3,0.016897
HashTable,sorted,insert,4,0.016796
HashTable,sorted,insert,5,0.020424
HashTable,sorted,find,1,0.000285
HashTable,sorted,find,2,0.000152
HashTable,sorted,find,3,0.00015
HashTable,sorted,find,4,0.00015
HashTable,sorted,find,5,0.000157
HashTable,sorted,delete,1,0.000182
HashTable,sorted,delete,2,0.000156
HashTable,sorted,delete,3,0.000104
HashTable,sorted,delete,4,0.000103
HashTable,sorted,delete,5,0.000103
BST,shuffled,insert,1,0.024171
BST,shuffled,insert,2,0.021608
BST,shuffled,insert,3,0.025746
BST,shuffled,insert,4,0.017484
BST,shuffled,insert,5,0.018589
BST,shuffled,find,1,0.000218
BST,shuffled,find,2,0.000122
BST,shuffled,find,3,0.000139
BST,shuffled,find,4,0.000117
BST,shuffled,find,5,0.000121
BST,shuffled,delete,1,0.000108
BST,shuffled,delete,2,7.3e-05
BST,shuffled,delete,3,6.8e-05
BST,shuffled,delete,4,6.8e-05
BST,shuffled,delete,5,6.8e-05
BST,sorted,insert,1,4.498011
BST,sorted,insert,2,4.60869
BST,sorted,insert,3,4.558911
BST,sorted,insert,4,4.703444
BST,sorted,insert,5,4.830993
BST,sorted,find,1,0.04103
BST,sorted,find,2,0.039717
BST,sorted,find,3,0.040988
BST,sorted,find,4,0.0401
BST,sorted,find,5,0.042171
BST,sorted,delete,1,0.026369
BST,sorted,delete,2,0.025809
BST,sorted,delete,3,0.027687
BST,sorted,delete,4,0.025919
BST,sorted,delete,5,0.026676
1 Structure Mode Operation Run Time_sec
2 LinkedList shuffled insert 1 2.818623
3 LinkedList shuffled insert 2 2.776622
4 LinkedList shuffled insert 3 2.932603
5 LinkedList shuffled insert 4 3.150283
6 LinkedList shuffled insert 5 3.077142
7 LinkedList shuffled find 1 0.032263
8 LinkedList shuffled find 2 0.0328
9 LinkedList shuffled find 3 0.03149
10 LinkedList shuffled find 4 0.031703
11 LinkedList shuffled find 5 0.031978
12 LinkedList shuffled delete 1 0.017758
13 LinkedList shuffled delete 2 0.032563
14 LinkedList shuffled delete 3 0.032256
15 LinkedList shuffled delete 4 0.032048
16 LinkedList shuffled delete 5 0.032178
17 LinkedList sorted insert 1 1.833796
18 LinkedList sorted insert 2 1.836535
19 LinkedList sorted insert 3 2.088518
20 LinkedList sorted insert 4 1.868659
21 LinkedList sorted insert 5 2.096095
22 LinkedList sorted find 1 0.021973
23 LinkedList sorted find 2 0.021922
24 LinkedList sorted find 3 0.022304
25 LinkedList sorted find 4 0.02182
26 LinkedList sorted find 5 0.021948
27 LinkedList sorted delete 1 0.008794
28 LinkedList sorted delete 2 0.021849
29 LinkedList sorted delete 3 0.021325
30 LinkedList sorted delete 4 0.021476
31 LinkedList sorted delete 5 0.028157
32 HashTable shuffled insert 1 0.022874
33 HashTable shuffled insert 2 0.021475
34 HashTable shuffled insert 3 0.019544
35 HashTable shuffled insert 4 0.023367
36 HashTable shuffled insert 5 0.029821
37 HashTable shuffled find 1 0.000252
38 HashTable shuffled find 2 0.000152
39 HashTable shuffled find 3 0.000143
40 HashTable shuffled find 4 0.000216
41 HashTable shuffled find 5 0.000147
42 HashTable shuffled delete 1 0.000208
43 HashTable shuffled delete 2 0.000132
44 HashTable shuffled delete 3 0.000112
45 HashTable shuffled delete 4 0.000106
46 HashTable shuffled delete 5 0.000106
47 HashTable sorted insert 1 0.020814
48 HashTable sorted insert 2 0.017296
49 HashTable sorted insert 3 0.016897
50 HashTable sorted insert 4 0.016796
51 HashTable sorted insert 5 0.020424
52 HashTable sorted find 1 0.000285
53 HashTable sorted find 2 0.000152
54 HashTable sorted find 3 0.00015
55 HashTable sorted find 4 0.00015
56 HashTable sorted find 5 0.000157
57 HashTable sorted delete 1 0.000182
58 HashTable sorted delete 2 0.000156
59 HashTable sorted delete 3 0.000104
60 HashTable sorted delete 4 0.000103
61 HashTable sorted delete 5 0.000103
62 BST shuffled insert 1 0.024171
63 BST shuffled insert 2 0.021608
64 BST shuffled insert 3 0.025746
65 BST shuffled insert 4 0.017484
66 BST shuffled insert 5 0.018589
67 BST shuffled find 1 0.000218
68 BST shuffled find 2 0.000122
69 BST shuffled find 3 0.000139
70 BST shuffled find 4 0.000117
71 BST shuffled find 5 0.000121
72 BST shuffled delete 1 0.000108
73 BST shuffled delete 2 7.3e-05
74 BST shuffled delete 3 6.8e-05
75 BST shuffled delete 4 6.8e-05
76 BST shuffled delete 5 6.8e-05
77 BST sorted insert 1 4.498011
78 BST sorted insert 2 4.60869
79 BST sorted insert 3 4.558911
80 BST sorted insert 4 4.703444
81 BST sorted insert 5 4.830993
82 BST sorted find 1 0.04103
83 BST sorted find 2 0.039717
84 BST sorted find 3 0.040988
85 BST sorted find 4 0.0401
86 BST sorted find 5 0.042171
87 BST sorted delete 1 0.026369
88 BST sorted delete 2 0.025809
89 BST sorted delete 3 0.027687
90 BST sorted delete 4 0.025919
91 BST sorted delete 5 0.026676