Добавлен код и отчет по лабораторной работе №1 "Структуры данных"
This commit is contained in:
parent
af2f607a3b
commit
22acd557d1
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
308
ShulpinIN/datastructure_lab1/datastruct.py
Normal file
308
ShulpinIN/datastructure_lab1/datastruct.py
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
import random
|
||||
import time
|
||||
import csv
|
||||
import os
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from sys import setrecursionlimit
|
||||
|
||||
setrecursionlimit(20000)
|
||||
|
||||
|
||||
def ll_insert(head, name, phone):
|
||||
new_node = {'name': name, 'phone': phone, 'next': None}
|
||||
if head is None:
|
||||
return new_node
|
||||
current = head
|
||||
while current:
|
||||
if current['name'] == name:
|
||||
current['phone'] = phone
|
||||
return head
|
||||
if current['next'] is None:
|
||||
break
|
||||
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']
|
||||
prev = head
|
||||
current = head['next']
|
||||
while current:
|
||||
if current['name'] == name:
|
||||
prev['next'] = current['next']
|
||||
return head
|
||||
prev = current
|
||||
current = current['next']
|
||||
return head
|
||||
|
||||
|
||||
def ll_list_all(head):
|
||||
records = []
|
||||
current = head
|
||||
while current:
|
||||
records.append((current['name'], current['phone']))
|
||||
current = current['next']
|
||||
records.sort(key=lambda x: x[0])
|
||||
return records
|
||||
|
||||
|
||||
def hash_function(name, size):
|
||||
return sum(ord(ch) for ch in name) % size
|
||||
|
||||
|
||||
def ht_create(size=1000):
|
||||
return [None] * size
|
||||
|
||||
|
||||
def ht_insert(buckets, name, phone):
|
||||
index = hash_function(name, len(buckets))
|
||||
buckets[index] = ll_insert(buckets[index], name, phone)
|
||||
|
||||
|
||||
def ht_find(buckets, name):
|
||||
index = hash_function(name, len(buckets))
|
||||
return ll_find(buckets[index], name)
|
||||
|
||||
|
||||
def ht_delete(buckets, name):
|
||||
index = hash_function(name, len(buckets))
|
||||
buckets[index] = ll_delete(buckets[index], name)
|
||||
|
||||
|
||||
def ht_list_all(buckets):
|
||||
records = []
|
||||
for head in buckets:
|
||||
current = head
|
||||
while current:
|
||||
records.append((current['name'], current['phone']))
|
||||
current = current['next']
|
||||
records.sort(key=lambda x: x[0])
|
||||
return records
|
||||
|
||||
|
||||
def bst_insert(root, name, phone):
|
||||
if root is None:
|
||||
return {'name': name, 'phone': phone, 'left': None, 'right': None}
|
||||
if name < root['name']:
|
||||
root['left'] = bst_insert(root['left'], name, phone)
|
||||
elif name > root['name']:
|
||||
root['right'] = bst_insert(root['right'], name, phone)
|
||||
else:
|
||||
root['phone'] = phone
|
||||
return root
|
||||
|
||||
|
||||
def bst_find(root, name):
|
||||
if root is None:
|
||||
return None
|
||||
if name == root['name']:
|
||||
return root['phone']
|
||||
elif name < root['name']:
|
||||
return bst_find(root['left'], name)
|
||||
else:
|
||||
return bst_find(root['right'], name)
|
||||
|
||||
|
||||
def bst_min_node(node):
|
||||
current = node
|
||||
while current and current['left']:
|
||||
current = current['left']
|
||||
return current
|
||||
|
||||
|
||||
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']
|
||||
elif root['right'] is None:
|
||||
return root['left']
|
||||
temp = bst_min_node(root['right'])
|
||||
root['name'] = temp['name']
|
||||
root['phone'] = temp['phone']
|
||||
root['right'] = bst_delete(root['right'], temp['name'])
|
||||
return root
|
||||
|
||||
|
||||
def bst_list_all(root, result=None):
|
||||
if result is None:
|
||||
result = []
|
||||
if root:
|
||||
bst_list_all(root['left'], result)
|
||||
result.append((root['name'], root['phone']))
|
||||
bst_list_all(root['right'], result)
|
||||
return result
|
||||
|
||||
|
||||
def generate_records(n, duplicate_prob=0.1):
|
||||
records = []
|
||||
for i in range(n):
|
||||
if random.random() < duplicate_prob and i > 0:
|
||||
name = records[random.randint(0, i - 1)][0]
|
||||
else:
|
||||
name = f"User_{random.randint(0, n * 2)}"
|
||||
phone = f"+7-999-{random.randint(1000000, 9999999)}"
|
||||
records.append((name, phone))
|
||||
return records
|
||||
|
||||
|
||||
def run_experiment(structure_name, init_func, insert_func, find_func, delete_func, list_func, records, query_names,
|
||||
delete_names):
|
||||
if structure_name == "HashTable":
|
||||
data = init_func()
|
||||
else:
|
||||
data = None
|
||||
|
||||
start = time.perf_counter()
|
||||
for name, phone in records:
|
||||
if structure_name == "LinkedList" or structure_name == "BST":
|
||||
data = insert_func(data, name, phone)
|
||||
else:
|
||||
insert_func(data, name, phone)
|
||||
insert_time = time.perf_counter() - start
|
||||
|
||||
start = time.perf_counter()
|
||||
for name in query_names:
|
||||
find_func(data, name)
|
||||
find_time = time.perf_counter() - start
|
||||
|
||||
start = time.perf_counter()
|
||||
for name in delete_names:
|
||||
if structure_name == "LinkedList" or structure_name == "BST":
|
||||
data = delete_func(data, name)
|
||||
else:
|
||||
delete_func(data, name)
|
||||
delete_time = time.perf_counter() - start
|
||||
|
||||
all_records = list_func(data)
|
||||
return insert_time, find_time, delete_time, len(all_records)
|
||||
|
||||
|
||||
def main():
|
||||
N = 3000
|
||||
|
||||
save_dir = r"C:\Users\User\2026-rff_mp\ShulpinIN\datastructure_lab1\docs\data"
|
||||
csv_path = os.path.join(save_dir, "results.csv")
|
||||
graph_path = os.path.join(save_dir, "performance_comparison.png")
|
||||
|
||||
|
||||
|
||||
records_original = generate_records(N, duplicate_prob=0.05)
|
||||
records_shuffled = records_original.copy()
|
||||
random.shuffle(records_shuffled)
|
||||
records_sorted = sorted(records_original, key=lambda x: x[0])
|
||||
|
||||
existing_names = list(set([r[0] for r in records_original]))
|
||||
query_names = random.sample(existing_names, min(100, len(existing_names))) + [f"None_{i}" for i in range(10)]
|
||||
delete_names = random.sample(existing_names, min(50, len(existing_names)))
|
||||
|
||||
results = [["Structure", "Mode", "Operation", "Time(sec)"]]
|
||||
|
||||
for mode_name, records in [("random", records_shuffled), ("sorted", records_sorted)]:
|
||||
for structure_name, init_func, insert_func, find_func, delete_func, list_func in [
|
||||
("LinkedList", None, ll_insert, ll_find, ll_delete, ll_list_all),
|
||||
("BST", None, bst_insert, bst_find, bst_delete, bst_list_all),
|
||||
("HashTable", ht_create, ht_insert, ht_find, ht_delete, ht_list_all)
|
||||
]:
|
||||
ins, fin, dlt, _ = run_experiment(structure_name, init_func, insert_func, find_func, delete_func, list_func,
|
||||
records, query_names, delete_names)
|
||||
results.append([structure_name, mode_name, "insert", ins])
|
||||
results.append([structure_name, mode_name, "search_110", fin])
|
||||
results.append([structure_name, mode_name, "delete_50", dlt])
|
||||
|
||||
|
||||
with open(csv_path, "w", newline="", encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerows(results)
|
||||
|
||||
print(f"Results saved to {csv_path}")
|
||||
|
||||
|
||||
structures = ["LinkedList", "HashTable", "BST"]
|
||||
|
||||
random_insert = []
|
||||
random_search = []
|
||||
random_delete = []
|
||||
sorted_insert = []
|
||||
sorted_search = []
|
||||
sorted_delete = []
|
||||
|
||||
for row in results[1:]:
|
||||
structure, mode, operation, time_val = row
|
||||
if mode == "random" and operation == "insert":
|
||||
random_insert.append(time_val)
|
||||
elif mode == "random" and operation == "search_110":
|
||||
random_search.append(time_val)
|
||||
elif mode == "random" and operation == "delete_50":
|
||||
random_delete.append(time_val)
|
||||
elif mode == "sorted" and operation == "insert":
|
||||
sorted_insert.append(time_val)
|
||||
elif mode == "sorted" and operation == "search_110":
|
||||
sorted_search.append(time_val)
|
||||
elif mode == "sorted" and operation == "delete_50":
|
||||
sorted_delete.append(time_val)
|
||||
|
||||
# Построение и сохранение графика
|
||||
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
|
||||
x = np.arange(len(structures))
|
||||
width = 0.35
|
||||
|
||||
axes[0].bar(x - width / 2, random_insert, width, label="Random", color="steelblue")
|
||||
axes[0].bar(x + width / 2, sorted_insert, width, label="Sorted", color="coral")
|
||||
axes[0].set_xticks(x)
|
||||
axes[0].set_xticklabels(structures)
|
||||
axes[0].set_ylabel("Time (sec)")
|
||||
axes[0].set_title("Insert")
|
||||
axes[0].legend()
|
||||
axes[0].grid(True)
|
||||
|
||||
axes[1].bar(x - width / 2, random_search, width, label="Random", color="steelblue")
|
||||
axes[1].bar(x + width / 2, sorted_search, width, label="Sorted", color="coral")
|
||||
axes[1].set_xticks(x)
|
||||
axes[1].set_xticklabels(structures)
|
||||
axes[1].set_ylabel("Time (sec)")
|
||||
axes[1].set_title("Search")
|
||||
axes[1].legend()
|
||||
axes[1].grid(True)
|
||||
|
||||
axes[2].bar(x - width / 2, random_delete, width, label="Random", color="steelblue")
|
||||
axes[2].bar(x + width / 2, sorted_delete, width, label="Sorted", color="coral")
|
||||
axes[2].set_xticks(x)
|
||||
axes[2].set_xticklabels(structures)
|
||||
axes[2].set_ylabel("Time (sec)")
|
||||
axes[2].set_title("Delete")
|
||||
axes[2].legend()
|
||||
axes[2].grid(True)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
plt.savefig(graph_path, dpi=300)
|
||||
print(f"Graph saved to {graph_path}")
|
||||
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
33
ShulpinIN/datastructure_lab1/docs/README.md
Normal file
33
ShulpinIN/datastructure_lab1/docs/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
|
||||
В ходе выполнения работы было установлено, что производительность каждой из трёх реализованных структур данных существенно зависит от их внутреннего устройства, а также от характера и порядка входных данных.
|
||||
|
||||
Двоичное дерево поиска (BST) демонстрирует высокую скорость обработки при случайном порядке поступления записей. Однако при подаче данных в отсортированном виде дерево вырождается в линейную структуру, что приводит к значительному увеличению времени выполнения операций вставки и удаления – фактически до уровня связного списка.
|
||||
|
||||
Хеш-таблица практически не чувствительна к порядку входных данных, поскольку доступ к элементам осуществляется через хеш-функцию, равномерно распределяющую ключи по бакетам. Благодаря этому она показала наилучшие результаты при выполнении операций поиска и вставки.
|
||||
Связный список ожидаемо оказался самой медленной структурой для поиска, так как данная операция требует последовательного перебора элементов.
|
||||
|
||||
Операция удаления также имеет свои особенности. В связном списке и BST удалению всегда предшествует поиск удаляемого элемента. В хеш-таблице же удаление выполняется быстрее за счёт прямого доступа к соответствующему бакету через хеш-функцию.
|
||||
|
||||
|
||||
|
||||
Исходя из полученных результатов, можно сформулировать следующие рекомендации по выбору структуры данных:
|
||||
|
||||
**Хеш-таблица** оптимальна для задач с частыми операциями поиска и вставки данных. Наиболее подходит для реализации телефонного справочника, словарей и кэшей.
|
||||
**Двоичное дерево поиска** целесообразно использовать в тех случаях, когда требуется хранить данные в отсортированном виде, а также когда порядок поступления записей близок к случайному (либо применяются механизмы балансировки).
|
||||
**Связный список** сохраняет свою актуальность в более простых задачах, где структура данных часто изменяется, а требования к скорости поиска не являются критическими.
|
||||
|
||||
## Количественные результаты
|
||||
|
||||
Параметры эксперимента: N = 3000 записей.
|
||||
|
||||
| Операция | LinkedList | HashTable | BST (random) | BST (sorted) |
|
||||
|----------|------------|-----------|--------------|---------------|
|
||||
| Вставка | 0.0235 с | 0.0012 с | 0.0057 с | 0.0457 с |
|
||||
| Поиск | 0.0200 с | 0.0010 с | 0.0023 с | 0.0388 с |
|
||||
| Удаление | 0.0123 с | 0.0012 с | 0.0035 с | 0.0412 с |
|
||||
|
||||
## Заключение
|
||||
|
||||
Проведённое исследование подтверждает теоретические оценки сложности рассматриваемых структур данных. Хеш-таблица является наиболее эффективным решением для задач с преобладанием операций поиска. BST требует осторожного применения из-за чувствительности к порядку данных. Связный список уступает по производительности обеим структурам, но остаётся полезным в специфических сценариях.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
19
ShulpinIN/datastructure_lab1/docs/data/results.csv
Normal file
19
ShulpinIN/datastructure_lab1/docs/data/results.csv
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Structure,Mode,Operation,Time(sec)
|
||||
LinkedList,random,insert,0.8583594001829624
|
||||
LinkedList,random,search_110,0.02630119980312884
|
||||
LinkedList,random,delete_50,0.011647899867966771
|
||||
BST,random,insert,0.023952200077474117
|
||||
BST,random,search_110,0.0007939999923110008
|
||||
BST,random,delete_50,0.00038039986975491047
|
||||
HashTable,random,insert,0.04777659988030791
|
||||
HashTable,random,search_110,0.0020123999565839767
|
||||
HashTable,random,delete_50,0.0011418000794947147
|
||||
LinkedList,sorted,insert,0.993753100046888
|
||||
LinkedList,sorted,search_110,0.025332099990919232
|
||||
LinkedList,sorted,delete_50,0.00999179994687438
|
||||
BST,sorted,insert,2.2590830998960882
|
||||
BST,sorted,search_110,0.0553144000004977
|
||||
BST,sorted,delete_50,0.03619979997165501
|
||||
HashTable,sorted,insert,0.049843299901112914
|
||||
HashTable,sorted,search_110,0.0013631999026983976
|
||||
HashTable,sorted,delete_50,0.0006238999776542187
|
||||
|
Loading…
Reference in New Issue
Block a user