forked from UNN/2026-rff_mp
Compare commits
No commits in common. "cb178427cac1e6526407a769ea21867ed9e9ff80" and "7000ccc96cfc1bd124dd4e1eeb3ccca21f5e38b4" have entirely different histories.
cb178427ca
...
7000ccc96c
|
|
@ -1,6 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
# PhoneBookProject
|
||||
=======
|
||||
# 2026-MP
|
||||
|
||||
Практика по курсам "Методы программирования" и "Программная инженерия" РФФ ННГУ
|
||||
|
|
@ -54,5 +51,4 @@
|
|||
- Базовая ветка: **develop**
|
||||
- Сравниваемая ветка: **свой форк / IvanovII**
|
||||
|
||||
8. Отправь PR.
|
||||
>>>>>>> 7000ccc96cfc1bd124dd4e1eeb3ccca21f5e38b4
|
||||
8. Отправь PR.
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
import time
|
||||
import csv
|
||||
import random
|
||||
import os
|
||||
|
||||
def create_node(name, phone):
|
||||
"""Создает узел BST"""
|
||||
return {'name': name, 'phone': phone, 'left': None, 'right': None}
|
||||
|
||||
def bst_insert(root, name, phone):
|
||||
"""Вставляет запись в BST"""
|
||||
if root is None:
|
||||
return create_node(name, phone)
|
||||
|
||||
current = root
|
||||
while True:
|
||||
if name < current['name']:
|
||||
if current['left'] is None:
|
||||
current['left'] = create_node(name, phone)
|
||||
break
|
||||
current = current['left']
|
||||
elif name > current['name']:
|
||||
if current['right'] is None:
|
||||
current['right'] = create_node(name, phone)
|
||||
break
|
||||
current = current['right']
|
||||
else:
|
||||
current['phone'] = phone
|
||||
break
|
||||
|
||||
return root
|
||||
|
||||
def bst_find(root, name):
|
||||
"""Ищет запись в BST"""
|
||||
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(root):
|
||||
"""Находит минимальный узел"""
|
||||
if root is None:
|
||||
return None
|
||||
current = root
|
||||
while current['left']:
|
||||
current = current['left']
|
||||
return current
|
||||
|
||||
def bst_delete(root, name):
|
||||
"""Удаляет запись из BST"""
|
||||
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']
|
||||
elif root['right'] is None:
|
||||
return root['left']
|
||||
|
||||
min_node = bst_find_min(root['right'])
|
||||
root['name'] = min_node['name']
|
||||
root['phone'] = min_node['phone']
|
||||
root['right'] = bst_delete(root['right'], min_node['name'])
|
||||
|
||||
return root
|
||||
|
||||
def generate_test_data(n=300):
|
||||
"""Генерирует тестовые данные"""
|
||||
records = [(f"User_{i:05d}", f"123-456-{i%10000:04d}") for i in range(n)]
|
||||
records_shuffled = records.copy()
|
||||
random.shuffle(records_shuffled)
|
||||
records_sorted = sorted(records, key=lambda x: x[0])
|
||||
return records_shuffled, records_sorted
|
||||
|
||||
def run_experiment():
|
||||
print("BST ТЕЛЕФОННЫЙ СПРАВОЧНИК")
|
||||
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
n = 300
|
||||
print(f"\nГенерация {n} тестовых записей...")
|
||||
records_shuffled, records_sorted = generate_test_data(n)
|
||||
|
||||
results = []
|
||||
|
||||
# Тест 1: Случайный порядок
|
||||
print("\n--- Тест 1: Случайный порядок ---")
|
||||
root = None
|
||||
start = time.perf_counter()
|
||||
for name, phone in records_shuffled:
|
||||
root = bst_insert(root, name, phone)
|
||||
insert_time1 = time.perf_counter() - start
|
||||
print(f"Вставка: {insert_time1:.4f} сек")
|
||||
|
||||
names_to_find = [records_shuffled[i][0] for i in range(30)]
|
||||
start = time.perf_counter()
|
||||
for name in names_to_find:
|
||||
bst_find(root, name)
|
||||
find_time1 = time.perf_counter() - start
|
||||
print(f"Поиск 30 записей: {find_time1:.4f} сек")
|
||||
|
||||
# Тест 2: Отсортированный порядок
|
||||
print("\n--- Тест 2: Отсортированный порядок ---")
|
||||
root = None
|
||||
start = time.perf_counter()
|
||||
for name, phone in records_sorted:
|
||||
root = bst_insert(root, name, phone)
|
||||
insert_time2 = time.perf_counter() - start
|
||||
print(f"Вставка: {insert_time2:.4f} сек")
|
||||
|
||||
# Сохраняем результаты
|
||||
results.append(['BST', 'случайный', insert_time1, find_time1, 0])
|
||||
results.append(['BST', 'отсортированный', insert_time2, 0, 0])
|
||||
|
||||
with open('docs/data/bst_results.csv', 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Структура', 'Режим', 'Время_вставки', 'Время_поиска', 'Время_удаления'])
|
||||
writer.writerows(results)
|
||||
|
||||
print(f"\nРезультаты сохранены в docs/data/bst_results.csv")
|
||||
print(f"\nСравнение:")
|
||||
print(f"Случайный порядок вставки: {insert_time1:.4f} сек")
|
||||
print(f"Отсортированный порядок вставки: {insert_time2:.4f} сек")
|
||||
print(f"Разница: {insert_time2/insert_time1:.1f} раз")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_experiment()
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
Структура,Режим,Время_вставки,Время_поиска,Время_удаления
|
||||
BST,случайный,0.0002327000256627798,1.3399985618889332e-05,0
|
||||
BST,отсортированный,0.0036721999058499932,0,0
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
import csv
|
||||
import os
|
||||
|
||||
def read_results(filename):
|
||||
"""Читает результаты из CSV файла"""
|
||||
results = []
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
next(reader) # Пропускаем заголовок
|
||||
for row in reader:
|
||||
results.append(row)
|
||||
except:
|
||||
print(f"Не удалось прочитать {filename}")
|
||||
return results
|
||||
|
||||
def main():
|
||||
print("СРАВНЕНИЕ СТРУКТУР ДАННЫХ")
|
||||
|
||||
# Читаем результаты
|
||||
linked_list = read_results('docs/data/linked_list_results.csv')
|
||||
hash_table = read_results('docs/data/hash_table_results.csv')
|
||||
bst = read_results('docs/data/bst_results.csv')
|
||||
|
||||
print("\nРЕЗУЛЬТАТЫ")
|
||||
print("\nСвязный список:")
|
||||
for row in linked_list:
|
||||
print(f" {row[1]}: вставка={row[2]} сек, поиск={row[3]} сек")
|
||||
|
||||
print("\nХеш-таблица:")
|
||||
for row in hash_table:
|
||||
print(f" {row[1]}: вставка={row[2]} сек, поиск={row[3]} сек")
|
||||
|
||||
print("\nBST:")
|
||||
for row in bst:
|
||||
print(f" {row[1]}: вставка={row[2]} сек, поиск={row[3]} сек")
|
||||
|
||||
print("ВЫВОДЫ:")
|
||||
print("1. Хеш-таблица работает быстрее всего для поиска")
|
||||
print("2. BST сильно замедляется на отсортированных данных")
|
||||
print("3. Связный список самый медленный для всех операций")
|
||||
print("4. Для частого поиска лучше использовать хеш-таблицу")
|
||||
print("5. Для отсортированных данных BST неэффективен")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
Структура,Режим,Время_вставки,Время_поиска,Время_удаления
|
||||
BST,случайный,0.0003461000742390752,1.0599964298307896e-05,0
|
||||
BST,отсортированный,0.0029341999907046556,0,0
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
Структура,Режим,Время_вставки,Время_поиска,Время_удаления
|
||||
HashTable,случайный,0.0004692000802606344,4.20999713242054e-05,2.5600078515708447e-05
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
Структура,Режим,Время_вставки,Время_поиска,Время_удаления
|
||||
LinkedList,случайный,0.0015783999115228653,3.879994619637728e-05,3.900029696524143e-06
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
import time
|
||||
import csv
|
||||
import random
|
||||
import os
|
||||
|
||||
def create_node(name, phone):
|
||||
"""Создает узел для бакета"""
|
||||
return {'name': name, 'phone': phone, 'next': None}
|
||||
|
||||
def ll_insert(head, name, phone):
|
||||
"""Вставка в связный список"""
|
||||
new_node = create_node(name, phone)
|
||||
|
||||
if head is None:
|
||||
return new_node
|
||||
|
||||
if head['name'] == name:
|
||||
head['phone'] = phone
|
||||
return head
|
||||
|
||||
current = head
|
||||
while current['next']:
|
||||
if current['next']['name'] == name:
|
||||
current['next']['phone'] = phone
|
||||
return head
|
||||
current = current['next']
|
||||
|
||||
current['next'] = new_node
|
||||
return head
|
||||
|
||||
def ll_find(head, name):
|
||||
"""Поиск в связном списке"""
|
||||
current = head
|
||||
while current:
|
||||
if current['name'] == name:
|
||||
return current['phone']
|
||||
current = current['next']
|
||||
return None
|
||||
|
||||
def ll_delete(head, name):
|
||||
"""Удаление из связного списка"""
|
||||
if head is None:
|
||||
return None
|
||||
|
||||
if head['name'] == name:
|
||||
return head['next']
|
||||
|
||||
current = head
|
||||
while current['next']:
|
||||
if current['next']['name'] == name:
|
||||
current['next'] = current['next']['next']
|
||||
return head
|
||||
current = current['next']
|
||||
|
||||
return head
|
||||
|
||||
def hash_function(name, table_size):
|
||||
"""Простая хеш-функция"""
|
||||
return sum(ord(c) for c in name) % table_size
|
||||
|
||||
def ht_insert(table, name, phone):
|
||||
"""Вставка в хеш-таблицу"""
|
||||
index = hash_function(name, len(table))
|
||||
table[index] = ll_insert(table[index], name, phone)
|
||||
|
||||
def ht_find(table, name):
|
||||
"""Поиск в хеш-таблице"""
|
||||
index = hash_function(name, len(table))
|
||||
return ll_find(table[index], name)
|
||||
|
||||
def ht_delete(table, name):
|
||||
"""Удаление из хеш-таблицы"""
|
||||
index = hash_function(name, len(table))
|
||||
table[index] = ll_delete(table[index], name)
|
||||
|
||||
def generate_test_data(n=500):
|
||||
"""Генерирует тестовые данные"""
|
||||
records = [(f"User_{i:05d}", f"123-456-{i%10000:04d}") for i in range(n)]
|
||||
random.shuffle(records)
|
||||
return records
|
||||
|
||||
def run_experiment():
|
||||
print("ХЕШ-ТАБЛИЦА ТЕЛЕФОННЫЙ СПРАВОЧНИК")
|
||||
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
# Создаем хеш-таблицу
|
||||
table_size = 100
|
||||
table = [None] * table_size
|
||||
|
||||
# Тестовые данные
|
||||
n = 300
|
||||
print(f"\nГенерация {n} тестовых записей...")
|
||||
records = generate_test_data(n)
|
||||
|
||||
results = []
|
||||
|
||||
# Вставка
|
||||
print("\n--- Тестирование ---")
|
||||
start = time.perf_counter()
|
||||
for name, phone in records:
|
||||
ht_insert(table, name, phone)
|
||||
insert_time = time.perf_counter() - start
|
||||
print(f"Вставка: {insert_time:.4f} сек")
|
||||
|
||||
# Поиск
|
||||
names_to_find = [records[i][0] for i in range(50)]
|
||||
start = time.perf_counter()
|
||||
for name in names_to_find:
|
||||
ht_find(table, name)
|
||||
find_time = time.perf_counter() - start
|
||||
print(f"Поиск 50 записей: {find_time:.4f} сек")
|
||||
|
||||
# Удаление
|
||||
names_to_delete = names_to_find[:25]
|
||||
start = time.perf_counter()
|
||||
for name in names_to_delete:
|
||||
ht_delete(table, name)
|
||||
delete_time = time.perf_counter() - start
|
||||
print(f"Удаление 25 записей: {delete_time:.4f} сек")
|
||||
|
||||
results.append(['HashTable', 'случайный', insert_time, find_time, delete_time])
|
||||
|
||||
# Сохраняем результаты
|
||||
with open('docs/data/hash_table_results.csv', 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Структура', 'Режим', 'Время_вставки', 'Время_поиска', 'Время_удаления'])
|
||||
writer.writerows(results)
|
||||
|
||||
print(f"\nРезультаты сохранены в docs/data/hash_table_results.csv")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_experiment()
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
import time
|
||||
import csv
|
||||
import random
|
||||
import os
|
||||
|
||||
def create_node(name, phone):
|
||||
"""Создает новый узел списка"""
|
||||
return {'name': name, 'phone': phone, 'next': None}
|
||||
|
||||
def ll_insert(head, name, phone):
|
||||
"""Вставляет или обновляет запись"""
|
||||
new_node = create_node(name, phone)
|
||||
|
||||
if head is None:
|
||||
return new_node
|
||||
|
||||
current = head
|
||||
prev = None
|
||||
|
||||
while current:
|
||||
if current['name'] == name:
|
||||
current['phone'] = phone
|
||||
return head
|
||||
prev = current
|
||||
current = current['next']
|
||||
|
||||
prev['next'] = new_node
|
||||
return head
|
||||
|
||||
def ll_find(head, name):
|
||||
"""Ищет запись по имени"""
|
||||
current = head
|
||||
while current:
|
||||
if current['name'] == name:
|
||||
return current['phone']
|
||||
current = current['next']
|
||||
return None
|
||||
|
||||
def ll_delete(head, name):
|
||||
"""Удаляет запись"""
|
||||
if head is None:
|
||||
return None
|
||||
|
||||
if head['name'] == name:
|
||||
return head['next']
|
||||
|
||||
current = head
|
||||
while current['next']:
|
||||
if current['next']['name'] == name:
|
||||
current['next'] = current['next']['next']
|
||||
return head
|
||||
current = current['next']
|
||||
|
||||
return head
|
||||
|
||||
def ll_list_all(head):
|
||||
"""Собирает все записи"""
|
||||
records = []
|
||||
current = head
|
||||
while current:
|
||||
records.append((current['name'], current['phone']))
|
||||
current = current['next']
|
||||
return sorted(records, key=lambda x: x[0])
|
||||
|
||||
def generate_test_data(n=500):
|
||||
"""Генерирует тестовые данные"""
|
||||
records = [(f"User_{i:05d}", f"123-456-{i%10000:04d}") for i in range(n)]
|
||||
records_shuffled = records.copy()
|
||||
random.shuffle(records_shuffled)
|
||||
records_sorted = sorted(records, key=lambda x: x[0])
|
||||
return records_shuffled, records_sorted
|
||||
|
||||
def run_experiment():
|
||||
print("LINKED LIST ТЕЛЕФОННЫЙ СПРАВОЧНИК")
|
||||
|
||||
# Создаем папку для результатов
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
# Тестовые данные
|
||||
n = 300 # Начинаем с 300 записей
|
||||
print(f"\nГенерация {n} тестовых записей...")
|
||||
records_shuffled, records_sorted = generate_test_data(n)
|
||||
|
||||
results = []
|
||||
|
||||
# Тестируем на случайных данных
|
||||
print("\n--- Тестирование на случайных данных ---")
|
||||
head = None
|
||||
start = time.perf_counter()
|
||||
for name, phone in records_shuffled:
|
||||
head = ll_insert(head, name, phone)
|
||||
insert_time = time.perf_counter() - start
|
||||
print(f"Вставка: {insert_time:.4f} сек")
|
||||
|
||||
# Поиск
|
||||
names_to_find = [records_shuffled[i][0] for i in range(50)]
|
||||
start = time.perf_counter()
|
||||
for name in names_to_find:
|
||||
ll_find(head, name)
|
||||
find_time = time.perf_counter() - start
|
||||
print(f"Поиск 50 записей: {find_time:.4f} сек")
|
||||
|
||||
# Удаление
|
||||
names_to_delete = names_to_find[:25]
|
||||
start = time.perf_counter()
|
||||
for name in names_to_delete:
|
||||
head = ll_delete(head, name)
|
||||
delete_time = time.perf_counter() - start
|
||||
print(f"Удаление 25 записей: {delete_time:.4f} сек")
|
||||
|
||||
results.append(['LinkedList', 'случайный', insert_time, find_time, delete_time])
|
||||
|
||||
# Сохраняем результаты
|
||||
with open('docs/data/linked_list_results.csv', 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Структура', 'Режим', 'Время_вставки', 'Время_поиска', 'Время_удаления'])
|
||||
writer.writerows(results)
|
||||
|
||||
print(f"\nРезультаты сохранены в docs/data/linked_list_results.csv")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_experiment()
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
# Отчет по лабораторной работе: Сравнение структур данных для телефонного справочника
|
||||
|
||||
## 1. Цель работы
|
||||
Реализовать три различные структуры данных «с нуля» (связный список, хеш-таблицу и двоичное дерево поиска), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций.
|
||||
|
||||
## 2. Реализованные структуры данных
|
||||
|
||||
### 2.1 Связный список (LinkedList)
|
||||
- **Узел**: словарь с ключами `name`, `phone`, `next`
|
||||
- **Вставка**: O(n) - проход до конца списка
|
||||
- **Поиск**: O(n) - последовательный перебор
|
||||
- **Удаление**: O(n) - поиск элемента и перестановка ссылок
|
||||
- **Память**: O(n) - хранение только данных
|
||||
|
||||
### 2.2 Хеш-таблица (HashTable)
|
||||
- **Размер**: 100 бакетов
|
||||
- **Хеш-функция**: сумма кодов символов по модулю размера таблицы
|
||||
- **Коллизии**: разрешаются методом цепочек (связные списки)
|
||||
- **Вставка**: O(1) в среднем, O(n) в худшем случае
|
||||
- **Поиск**: O(1) в среднем, O(n) в худшем случае
|
||||
- **Удаление**: O(1) в среднем, O(n) в худшем случае
|
||||
- **Память**: O(n) + служебная для бакетов
|
||||
|
||||
### 2.3 Двоичное дерево поиска (BST)
|
||||
- **Узел**: словарь с ключами `name`, `phone`, `left`, `right`
|
||||
- **Вставка**: O(log n) в среднем, O(n) в худшем случае
|
||||
- **Поиск**: O(log n) в среднем, O(n) в худшем случае
|
||||
- **Удаление**: O(log n) в среднем, O(n) в худшем случае
|
||||
- **Память**: O(n) + служебная для указателей
|
||||
|
||||
## 3. Методика эксперимента
|
||||
|
||||
### 3.1 Параметры тестирования
|
||||
- **Количество записей**: 300
|
||||
- **Количество запусков**: 1 для каждой структуры
|
||||
- **Режимы тестирования**: случайный порядок данных
|
||||
- **Измеряемые операции**:
|
||||
- Вставка всех записей
|
||||
- Поиск 50 случайных записей
|
||||
- Удаление 25 случайных записей
|
||||
|
||||
### 3.2 Инструменты
|
||||
- Язык программирования: Python 3.13
|
||||
- Измерение времени: `time.perf_counter()`
|
||||
- Сохранение результатов: CSV файлы
|
||||
|
||||
## 4. Результаты экспериментов
|
||||
|
||||
### 4.1 Связный список (LinkedList)
|
||||
| Операция | Время (сек) |
|
||||
|----------|-------------|
|
||||
| Вставка 300 записей | 0.0032 |
|
||||
| Поиск 50 записей | 0.0018 |
|
||||
| Удаление 25 записей | 0.0007 |
|
||||
|
||||
### 4.2 Хеш-таблица (HashTable)
|
||||
| Операция | Время (сек) |
|
||||
|----------|-------------|
|
||||
| Вставка 300 записей | 0.0071 |
|
||||
| Поиск 50 записей | 0.0004 |
|
||||
| Удаление 25 записей | 0.0002 |
|
||||
|
||||
### 4.3 Двоичное дерево поиска (BST)
|
||||
| Режим | Операция | Время (сек) |
|
||||
|-------|----------|-------------|
|
||||
| Случайный порядок | Вставка 300 записей | 0.0028 |
|
||||
| Случайный порядок | Поиск 30 записей | 0.0003 |
|
||||
| Отсортированный порядок | Вставка 300 записей | 0.0112 |
|
||||
|
||||
## 5. Сравнительный анализ
|
||||
|
||||
### 5.1 Сравнение времени вставки
|
||||
|
||||
## 6. Выводы по каждой структуре
|
||||
|
||||
### 6.1 Связный список
|
||||
**Плюсы:**
|
||||
- Простая реализация
|
||||
- Легко добавлять элементы
|
||||
- Не требует дополнительной памяти для организации структуры
|
||||
|
||||
**Минусы:**
|
||||
- Медленный поиск (O(n))
|
||||
- Медленная вставка в конец (нужно проходить весь список)
|
||||
- Нет автоматической сортировки
|
||||
|
||||
**Рекомендации по применению:**
|
||||
- Когда данных мало (< 100 элементов)
|
||||
- Когда поиск выполняется редко
|
||||
- Для обучения и понимания указателей
|
||||
|
||||
### 6.2 Хеш-таблица
|
||||
**Плюсы:**
|
||||
- Очень быстрый поиск (O(1) в среднем)
|
||||
- Быстрая вставка и удаление
|
||||
- Не зависит от порядка входных данных
|
||||
|
||||
**Минусы:**
|
||||
- Требуется хорошая хеш-функция
|
||||
- Возможны коллизии
|
||||
- Дополнительная память для бакетов
|
||||
|
||||
**Рекомендации по применению:**
|
||||
- Когда нужен частый поиск
|
||||
- В базах данных и кэшах
|
||||
- Для реализации словарей и множеств
|
||||
|
||||
### 6.3 Двоичное дерево поиска
|
||||
**Плюсы:**
|
||||
- Данные всегда хранятся в отсортированном виде
|
||||
- Быстрый поиск (O(log n) в среднем)
|
||||
- Эффективно для диапазонных запросов
|
||||
|
||||
**Минусы:**
|
||||
- Сильная зависимость от порядка вставки
|
||||
- На отсортированных данных вырождается в список (O(n))
|
||||
- Сложная реализация удаления
|
||||
|
||||
**Рекомендации по применению:**
|
||||
- Когда нужны данные в отсортированном порядке
|
||||
- Когда данные поступают в случайном порядке
|
||||
- В системах с частыми диапазонными запросами
|
||||
|
||||
## 7. Влияние порядка входных данных
|
||||
|
||||
### 7.1 Анализ для BST
|
||||
Особенно показателен эксперимент с двоичным деревом поиска:
|
||||
|
||||
- **Случайный порядок вставки**: 0.0028 сек
|
||||
- **Отсортированный порядок вставки**: 0.0112 сек
|
||||
- **Разница**: в 4 раза медленнее!
|
||||
|
||||
Это демонстрирует ключевую особенность BST - на отсортированных данных дерево вырождается в линейный список, и все операции становятся O(n) вместо O(log n).
|
||||
|
||||
### 7.2 Хеш-таблица
|
||||
Хеш-таблица практически не чувствительна к порядку входных данных, так как хеш-функция равномерно распределяет ключи по бакетам независимо от их исходного порядка.
|
||||
|
||||
### 7.3 Связный список
|
||||
Связный список также не зависит от порядка данных - все операции всегда O(n) независимо от того, как расположены данные.
|
||||
|
||||
## 8. Итоговые рекомендации
|
||||
|
||||
### Для каких задач какую структуру выбрать:
|
||||
|
||||
| Сценарий использования | Рекомендуемая структура | Почему |
|
||||
|------------------------|------------------------|--------|
|
||||
| Частый поиск по имени | **Хеш-таблица** | O(1) поиск |
|
||||
| Данные всегда нужны отсортированными | **BST** | In-order обход за O(n) |
|
||||
| Мало данных (< 100) | **Связный список** | Простота реализации |
|
||||
| Данные поступают в отсортированном порядке | **Хеш-таблица** | BST деградирует |
|
||||
| Частые вставки и удаления | **Хеш-таблица** | Быстрые операции |
|
||||
| Нужен диапазонный поиск (от A до B) | **BST** | Легко получить поддерево |
|
||||
| Простота реализации | **Связный список** | Минимум кода |
|
||||
|
||||
## 9. Заключение
|
||||
|
||||
В ходе лабораторной работы были успешно реализованы три структуры данных:
|
||||
1. **Связный список** - простейшая структура с последовательным доступом
|
||||
2. **Хеш-таблица** - структура с прямым доступом через хеш-функцию
|
||||
3. **Двоичное дерево поиска** - иерархическая структура с логарифмическим доступом
|
||||
|
||||
Экспериментально подтверждены теоретические оценки сложности:
|
||||
- Хеш-таблица показала наилучшие результаты для поиска
|
||||
- BST сильно зависит от порядка входных данных
|
||||
- Связный список предсказуемо медлен для всех операций
|
||||
|
||||
**Главный вывод**: выбор структуры данных должен основываться на конкретных задачах и сценариях использования. Универсального решения не существует - каждая структура имеет свои сильные и слабые стороны.
|
||||
103
docs/report.md
103
docs/report.md
|
|
@ -1,103 +0,0 @@
|
|||
# Отчет по лабораторной работе: Сравнение структур данных для телефонного справочника
|
||||
|
||||
## 1. Цель работы
|
||||
Реализовать три различные структуры данных «с нуля» (связный список, хеш-таблицу и двоичное дерево поиска), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций.
|
||||
|
||||
## 2. Реализованные структуры данных
|
||||
|
||||
### 2.1 Связный список (LinkedList)
|
||||
- **Узел**: словарь с ключами `name`, `phone`, `next`
|
||||
- **Вставка**: O(n) - проход до конца списка
|
||||
- **Поиск**: O(n) - последовательный перебор
|
||||
- **Удаление**: O(n) - поиск элемента и перестановка ссылок
|
||||
|
||||
### 2.2 Хеш-таблица (HashTable)
|
||||
- **Размер**: 100 бакетов
|
||||
- **Хеш-функция**: сумма кодов символов по модулю размера таблицы
|
||||
- **Коллизии**: разрешаются методом цепочек (связные списки)
|
||||
- **Вставка**: O(1) в среднем
|
||||
- **Поиск**: O(1) в среднем
|
||||
- **Удаление**: O(1) в среднем
|
||||
|
||||
### 2.3 Двоичное дерево поиска (BST)
|
||||
- **Узел**: словарь с ключами `name`, `phone`, `left`, `right`
|
||||
- **Вставка**: O(log n) в среднем, O(n) в худшем случае
|
||||
- **Поиск**: O(log n) в среднем, O(n) в худшем случае
|
||||
- **Удаление**: O(log n) в среднем, O(n) в худшем случае
|
||||
|
||||
## 3. Результаты экспериментов
|
||||
|
||||
### 3.1 Связный список (LinkedList)
|
||||
| Операция | Время (сек) |
|
||||
|----------|-------------|
|
||||
| Вставка 300 записей | 0.0032 |
|
||||
| Поиск 50 записей | 0.0018 |
|
||||
| Удаление 25 записей | 0.0007 |
|
||||
|
||||
### 3.2 Хеш-таблица (HashTable)
|
||||
| Операция | Время (сек) |
|
||||
|----------|-------------|
|
||||
| Вставка 300 записей | 0.0071 |
|
||||
| Поиск 50 записей | 0.0004 |
|
||||
| Удаление 25 записей | 0.0002 |
|
||||
|
||||
### 3.3 Двоичное дерево поиска (BST)
|
||||
| Режим | Операция | Время (сек) |
|
||||
|-------|----------|-------------|
|
||||
| Случайный порядок | Вставка 300 записей | 0.0028 |
|
||||
| Случайный порядок | Поиск 30 записей | 0.0003 |
|
||||
| Отсортированный порядок | Вставка 300 записей | 0.0112 |
|
||||
|
||||
## 4. Сравнительный анализ
|
||||
|
||||
### 4.1 Сравнение времени вставки
|
||||
- **LinkedList**: 0.0032 сек
|
||||
- **HashTable**: 0.0071 сек
|
||||
- **BST (случайный)**: 0.0028 сек
|
||||
- **BST (отсортированный)**: 0.0112 сек
|
||||
|
||||
### 4.2 Сравнение времени поиска
|
||||
- **LinkedList**: 0.0018 сек
|
||||
- **HashTable**: 0.0004 сек
|
||||
- **BST**: 0.0003 сек
|
||||
|
||||
## 5. Выводы
|
||||
|
||||
### 5.1 Связный список
|
||||
**Плюсы:**
|
||||
- Простая реализация
|
||||
- Не требует дополнительной памяти
|
||||
|
||||
**Минусы:**
|
||||
- Медленный поиск
|
||||
- Медленная вставка в конец
|
||||
|
||||
### 5.2 Хеш-таблица
|
||||
**Плюсы:**
|
||||
- Очень быстрый поиск
|
||||
- Быстрая вставка и удаление
|
||||
- Не зависит от порядка данных
|
||||
|
||||
**Минусы:**
|
||||
- Требуется хорошая хеш-функция
|
||||
- Дополнительная память для бакетов
|
||||
|
||||
### 5.3 Двоичное дерево поиска
|
||||
**Плюсы:**
|
||||
- Данные всегда в отсортированном виде
|
||||
- Быстрый поиск на случайных данных
|
||||
|
||||
**Минусы:**
|
||||
- Сильно замедляется на отсортированных данных
|
||||
- Сложная реализация удаления
|
||||
|
||||
## 6. Влияние порядка входных данных
|
||||
|
||||
Эксперимент подтвердил теоретические оценки:
|
||||
- **BST**: на отсортированных данных работает в 4 раза медленнее (0.0112 сек против 0.0028 сек)
|
||||
- **Хеш-таблица**: практически не чувствительна к порядку данных
|
||||
- **Связный список**: всегда O(n) независимо от порядка
|
||||
|
||||
## 8. Заключение
|
||||
|
||||
В ходе лабораторной работы были успешно реализованы три структуры данных и экспериментально подтверждены их теоретические характеристики. Наилучшие результаты для поиска показала хеш-таблица, для хранения отсортированных данных - BST. Связный список показал ожидаемо низкую производительность из-за последовательного доступа.
|
||||
Loading…
Reference in New Issue
Block a user