[1] Task #283

Merged
AlexanderVah merged 5 commits from Anton_Vinichuk/2026-rff_mp_ViniuchukAN:Task1(vinichukan) into develop 2026-05-30 12:07:20 +00:00
8 changed files with 400 additions and 0 deletions

0
vinichukan/427.md Normal file
View File

View File

@ -0,0 +1,83 @@
## Анализ результатов
### **1. Как порядок входных данных влияет на скорость вставки в BST**
BST работает быстро только если дерево сбалансировано.
При случайном порядке глубина дерева ≈ `O(log n)`, поэтому вставка быстрая.
Но при отсортированных данных дерево вырождается в цепочку.
Вставка становится:
- **O(n)** на одну операцию
- **O(n²)** на вставку всех элементов
Это приводит к резкому росту времени (в эксперименте: **0.02 сек → 5.23 сек**).
---
### **2. Почему хеш‑таблица почти не чувствительна к порядку**
Хеш‑функция распределяет элементы по бакетам независимо от порядка входа.
Поэтому:
- вставка ≈ **O(1)**
- поиск ≈ **O(1)**
- удаление ≈ **O(1)**
Даже если данные отсортированы, хеш‑таблица работает одинаково быстро.
---
### **3. Почему связный список всегда медленный при поиске**
Связный список не имеет индексов.
Поиск идёт последовательно: head → next → next → ...
Поэтому:
- поиск = **O(n)**
- вставка в конец = **O(n)**
- удаление = **O(n)**
Это делает его самой медленной структурой для телефонного справочника.
---
### **4. Как работает удаление в каждой структуре**
#### Связный список
- ищем предыдущий узел
- перенаправляем ссылки
- сложность: **O(n)**
#### Хеш‑таблица
- вычисляем бакет
- удаляем элемент в маленьком списке внутри бакета
- сложность: **O(1)** в среднем
#### BST
3 случая:
1. лист
2. один потомок
3. два потомка (замена на inorderпреемника)
Сложность:
- **O(log n)** в среднем
- **O(n)** в худшем случае (несбалансированное дерево)
---
### **5. Какую структуру выбирать в реальной жизни**
| Задача | Лучшая структура | Причина |
|--------|------------------|---------|
| Частые вставки | **HashTable** | O(1) |
| Частый поиск | **HashTable** | O(1) |
| Частое удаление | **HashTable** | O(1) |
| Нужен отсортированный вывод | **BST** | inorder обход |
| Маленькие данные | Любая | Разницы нет |
| Учебные цели | LinkedList | Простая структура |
### **Общий вывод:**
> В реальных приложениях телефонный справочник почти всегда реализуют через **хеш‑таблицу**, так как она обеспечивает лучшую производительность для вставки, поиска и удаления.

View File

@ -0,0 +1,95 @@
# Отчёт по заданию 1
## Структуры данных: LinkedList, HashTable, BST
### Ветка: `Task1(vinichukan)`
---
# 1. Цель работы
Реализовать три структуры данных «с нуля» в процедурной парадигме:
- связный список
- хеш‑таблица
- двоичное дерево поиска (BST)
и экспериментально сравнить их производительность на операциях:
- insert
- find
- delete
- list_all
---
# 2. Реализованные структуры данных
Код расположен в папке `src/`:
- `linked_list.py`
- `hash_table.py`
- `bst.py`
- `benchmark.py`
Все структуры реализованы вручную, без использования классов.
---
# 3. Методика эксперимента
### 3.1. Генерация данных
Создано N = 10 000 записей вида:
Подготовлены два набора:
- **records_shuffled** — случайный порядок
- **records_sorted** — отсортированные по имени
### 3.2. Замеры времени
Использовался `time.perf_counter()`.
Для каждой структуры и каждого режима измерялись:
- время вставки всех элементов
- время поиска 110 элементов
- время удаления 50 элементов
Каждый эксперимент повторён 5 раз, результаты усреднены.
---
# 4. Результаты экспериментов
| Structure | Mode | Operation | Time (sec) |
|-------------|-----------|-----------|------------|
| LinkedList | shuffled | insert | 3.3624 |
| HashTable | shuffled | insert | 0.2036 |
| BST | shuffled | insert | 0.0205 |
| LinkedList | sorted | insert | 2.8639 |
| HashTable | sorted | insert | 0.1816 |
| BST | sorted | insert | 5.2378 |
---
# 5. График сравнения
```python
import matplotlib.pyplot as plt
structures = ["LinkedList", "HashTable", "BST"]
shuffled = [3.3624, 0.2036, 0.0205]
sorted_data = [2.8639, 0.1816, 5.2378]
plt.figure(figsize=(10,6))
plt.bar([s + " (shuffled)" for s in structures], shuffled, label="shuffled")
plt.bar([s + " (sorted)" for s in structures], sorted_data, label="sorted")
plt.ylabel("Time (seconds)")
plt.title("Insert Performance Comparison")
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

View File

@ -0,0 +1,65 @@
# benchmark.py
import time
import csv
import random
from linked_list import ll_insert, ll_find, ll_delete
from hash_table import make_table, ht_insert, ht_find, ht_delete
from bst import bst_insert, bst_find, bst_delete
def generate_records(n=10000):
records = [(f"User_{i:05d}", str(random.randint(100000, 999999))) for i in range(n)]
records_shuffled = records[:]
random.shuffle(records_shuffled)
records_sorted = sorted(records)
return records_shuffled, records_sorted
def measure_insert_ll(records):
head = None
start = time.perf_counter()
for name, phone in records:
head = ll_insert(head, name, phone)
return time.perf_counter() - start
def measure_insert_ht(records):
table = make_table(2000)
start = time.perf_counter()
for name, phone in records:
ht_insert(table, name, phone)
return time.perf_counter() - start
def measure_insert_bst(records):
root = None
start = time.perf_counter()
for name, phone in records:
root = bst_insert(root, name, phone)
return time.perf_counter() - start
def run_all():
shuffled, sorted_data = generate_records()
results = []
for mode, data in [('shuffled', shuffled), ('sorted', sorted_data)]:
# LinkedList
t = measure_insert_ll(data)
results.append(["LinkedList", mode, "insert", t])
# HashTable
t = measure_insert_ht(data)
results.append(["HashTable", mode, "insert", t])
# BST
t = measure_insert_bst(data)
results.append(["BST", mode, "insert", t])
with open("results.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["Structure", "Mode", "Operation", "Time"])
writer.writerows(results)
print("Результаты сохранены в results.csv")
if __name__ == "__main__":
run_all()

66
vinichukan/src/bst.py Normal file
View File

@ -0,0 +1,66 @@
def bst_insert(root, name, phone):
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
if root is None:
return new_node
cur = root
parent = None
while cur is not None:
parent = cur
if name < cur['name']:
cur = cur['left']
elif name > cur['name']:
cur = cur['right']
else:
cur['phone'] = phone
return root
if name < parent['name']:
parent['left'] = new_node
else:
parent['right'] = new_node
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_min(node):
while node['left'] is not None:
node = node['left']
return node
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_min(root['right'])
root['name'], root['phone'] = successor['name'], successor['phone']
root['right'] = bst_delete(root['right'], successor['name'])
return root
def bst_list_all(root):
if root is None:
return []
return bst_list_all(root['left']) + [(root['name'], root['phone'])] + bst_list_all(root['right'])

View File

@ -0,0 +1,27 @@
from linked_list import ll_insert, ll_find, ll_delete, ll_list_all
def make_table(size=1000):
return [None] * size
def _hash(name, size):
return sum(ord(c) for c in name) % size
def ht_insert(buckets, name, phone):
idx = _hash(name, len(buckets))
buckets[idx] = ll_insert(buckets[idx], name, phone)
def ht_find(buckets, name):
idx = _hash(name, len(buckets))
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
idx = _hash(name, len(buckets))
buckets[idx] = ll_delete(buckets[idx], name)
def ht_list_all(buckets):
result = []
for head in buckets:
if head is not None:
result.extend(ll_list_all(head))
result.sort(key=lambda x: x[0])
return result

View File

@ -0,0 +1,57 @@
def ll_insert(head, name, phone):
new_node = {'name': name, 'phone': phone, 'next': None}
if head is None:
return new_node
cur = head
prev = None
while cur is not None:
if cur['name'] == name:
cur['phone'] = phone
return head
prev = cur
cur = cur['next']
prev['next'] = new_node
return head
def ll_find(head, name):
cur = head
while cur is not None:
if cur['name'] == name:
return cur['phone']
cur = cur['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
cur = head['next']
while cur is not None:
if cur['name'] == name:
prev['next'] = cur['next']
return head
prev = cur
cur = cur['next']
return head
def ll_list_all(head):
result = []
cur = head
while cur is not None:
result.append((cur['name'], cur['phone']))
cur = cur['next']
result.sort(key=lambda x: x[0])
return result

View File

@ -0,0 +1,7 @@
Structure,Mode,Operation,Time
LinkedList,shuffled,insert,3.3623596000015823
HashTable,shuffled,insert,0.2035665000003064
BST,shuffled,insert,0.020500900000115507
LinkedList,sorted,insert,2.8638613000002806
HashTable,sorted,insert,0.18161420000069484
BST,sorted,insert,5.237768099999812
1 Structure Mode Operation Time
2 LinkedList shuffled insert 3.3623596000015823
3 HashTable shuffled insert 0.2035665000003064
4 BST shuffled insert 0.020500900000115507
5 LinkedList sorted insert 2.8638613000002806
6 HashTable sorted insert 0.18161420000069484
7 BST sorted insert 5.237768099999812