[1] Task #283
0
vinichukan/427.md
Normal file
0
vinichukan/427.md
Normal file
83
vinichukan/docs/data/ conclusion.md
Normal file
83
vinichukan/docs/data/ conclusion.md
Normal 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** | in‑order обход |
|
||||
| Маленькие данные | Любая | Разницы нет |
|
||||
| Учебные цели | LinkedList | Простая структура |
|
||||
|
||||
### **Общий вывод:**
|
||||
> В реальных приложениях телефонный справочник почти всегда реализуют через **хеш‑таблицу**, так как она обеспечивает лучшую производительность для вставки, поиска и удаления.
|
||||
|
||||
95
vinichukan/docs/report_task1.md
Normal file
95
vinichukan/docs/report_task1.md
Normal 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()
|
||||
|
||||
|
||||
65
vinichukan/src/benchmark.py
Normal file
65
vinichukan/src/benchmark.py
Normal 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
66
vinichukan/src/bst.py
Normal 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'])
|
||||
27
vinichukan/src/hash_table.py
Normal file
27
vinichukan/src/hash_table.py
Normal 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
|
||||
57
vinichukan/src/linked_list.py
Normal file
57
vinichukan/src/linked_list.py
Normal 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
|
||||
7
vinichukan/src/results.csv
Normal file
7
vinichukan/src/results.csv
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user