diff --git a/ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml b/ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/ShulpinIN/datastructure_lab1/datastruct.py b/ShulpinIN/datastructure_lab1/datastruct.py new file mode 100644 index 0000000..09a05bf --- /dev/null +++ b/ShulpinIN/datastructure_lab1/datastruct.py @@ -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() \ No newline at end of file diff --git a/ShulpinIN/datastructure_lab1/docs/README.md b/ShulpinIN/datastructure_lab1/docs/README.md new file mode 100644 index 0000000..45a0a50 --- /dev/null +++ b/ShulpinIN/datastructure_lab1/docs/README.md @@ -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 требует осторожного применения из-за чувствительности к порядку данных. Связный список уступает по производительности обеим структурам, но остаётся полезным в специфических сценариях. + diff --git a/ShulpinIN/datastructure_lab1/docs/data/performance_comparison.png b/ShulpinIN/datastructure_lab1/docs/data/performance_comparison.png new file mode 100644 index 0000000..28a6cca Binary files /dev/null and b/ShulpinIN/datastructure_lab1/docs/data/performance_comparison.png differ diff --git a/ShulpinIN/datastructure_lab1/docs/data/results.csv b/ShulpinIN/datastructure_lab1/docs/data/results.csv new file mode 100644 index 0000000..389f296 --- /dev/null +++ b/ShulpinIN/datastructure_lab1/docs/data/results.csv @@ -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