7.4 KiB
Отчет по заданию 1: структуры данных
Цель
Реализовать три структуры данных с нуля в процедурной парадигме и сравнить скорость основных операций телефонного справочника:
insert(name, phone)- добавить или обновить запись;find(name)- найти телефон по имени;delete(name)- удалить запись;list_all()- получить все записи, отсортированные по имени.
Классы не использовались. Узлы связного списка и дерева представлены словарями, хеш-таблица представлена списком бакетов.
Реализация
Код находится в файле phonebook.py.
Реализованы функции:
- связный список:
ll_insert,ll_find,ll_delete,ll_list_all; - хеш-таблица:
create_hash_table,ht_insert,ht_find,ht_delete,ht_list_all; - двоичное дерево поиска:
bst_insert,bst_find,bst_delete,bst_list_all.
Для хеш-таблицы используется метод цепочек: каждый бакет хранит голову
связного списка. Хеш-функция написана вручную, чтобы результат не зависел от
рандомизации встроенной функции hash() в Python.
Для BST вставка, поиск, удаление и обход написаны без классов. Обход
bst_list_all реализован итеративно, чтобы отсортированный вход на 10000
элементов не приводил к переполнению стека рекурсии.
Методика эксперимента
Скрипт эксперимента находится в файле benchmark.py.
Параметры запуска:
- количество записей:
N = 10000; - число повторов каждого эксперимента:
5; - имена:
User_00000,User_00001, ...,User_09999; - два режима входных данных:
shuffledиsorted; - поиск: 100 существующих имен и 10 отсутствующих;
- удаление: 50 случайных существующих имен;
- размер хеш-таблицы:
20011бакетов.
После вставки структура не пересоздается: поиск и удаление выполняются на той же заполненной структуре. Для каждого режима и каждой структуры создается новая структура.
Файлы с результатами:
docs/data/results.csv- все отдельные замеры;docs/data/summary.csv- среднее время и список всех пяти замеров;docs/data/performance.svg- столбчатая диаграмма средних значений.
Средние результаты
Время указано в секундах.
| Структура | Режим | Вставка | Поиск | Удаление |
|---|---|---|---|---|
| LinkedList | shuffled | 1.555942 | 0.014180 | 0.006345 |
| LinkedList | sorted | 1.513740 | 0.012993 | 0.006041 |
| HashTable | shuffled | 0.005934 | 0.000056 | 0.000031 |
| HashTable | sorted | 0.006140 | 0.000056 | 0.000029 |
| BST | shuffled | 0.011223 | 0.000093 | 0.000069 |
| BST | sorted | 2.250442 | 0.019857 | 0.010803 |
Анализ
Связный список оказался самым медленным на вставке и поиске. Причина в том, что
для корректной операции insert нужно проверить, есть ли уже запись с таким
именем. При уникальных именах почти каждая вставка проходит по всему текущему
списку, поэтому суммарная сложность вставки всех записей становится O(n^2).
Порядок входных данных почти не влияет на результат, потому что структура не
использует порядок ключей.
Хеш-таблица показала лучшие результаты почти во всех операциях. При хорошем
распределении по бакетам вставка, поиск и удаление близки к O(1). Порядок
входных данных почти не влияет на время, так как индекс бакета определяется
хешем имени, а не расположением записи во входном списке.
BST хорошо работает на перемешанных данных: дерево получается сравнительно
сбалансированным, поэтому операции близки к O(log n). На отсортированном
входе обычное двоичное дерево поиска вырождается в цепочку: каждый новый ключ
становится правым потомком предыдущего. Из-за этого вставка всех записей
становится O(n^2), а поиск и удаление приближаются к поведению связного
списка.
Удаление у хеш-таблицы быстрое по той же причине, что и поиск: сначала вычисляется бакет, затем просматривается короткая цепочка. В BST удаление быстрое на перемешанном дереве, но на вырожденном дереве оно замедляется. В связном списке удаление требует линейного поиска удаляемого элемента.
Вывод
Для частого поиска, обновления и удаления по точному имени лучше выбирать хеш-таблицу. Она быстрее всего в эксперименте и почти не зависит от порядка вставки.
Если нужно часто получать данные в отсортированном порядке, дерево поиска дает
удобный in-order обход без отдельной сортировки. Но обычный BST чувствителен
к порядку входных данных, поэтому на практике лучше использовать
самобалансирующееся дерево или готовую структуру из библиотеки.
Связный список подходит только для маленьких наборов данных или учебных задач. Для телефонного справочника с частым поиском он неудачен, потому что каждая операция поиска требует последовательного прохода по элементам.