2026-rff_mp/novikovsd/answers.txt

25 lines
7.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

В двоичном дереве поиска (BST) порядок добавления элементов определяет форму дерева. Если данные поступают в случайном порядке, дерево получается примерно сбалансированным: высота ~ O(log N), и вставка выполняется за O(log N) в среднем.
Если же данные отсортированы (по возрастанию или убыванию), каждый новый элемент становится либо самым правым, либо самым левым потомком. Дерево вырождается в линейный связный список (так называемое "вырожденное дерево"). В этом случае вставка каждого нового элемента требует прохода по всем уже вставленным узлам, то есть O(N) на операцию. Суммарная вставка N элементов O(N²). В эксперименте для отсортированного режима BST будет работать значительно медленнее, чем для случайного.
В моей реализации bst_insert итеративная, но алгоритм сохраняет эту зависимость: при отсортированных именах (User_00000, User_00001, …) каждый новый ключ больше всех предыдущих, поэтому поиск места вставки каждый раз обходит всю текущую цепочку правых потомков, что приводит к квадратичной сложности.
Хеш‑таблица вычисляет индекс (хеш) от имени и сразу помещает запись в соответствующую корзину. Порядок поступления данных никак не влияет на значение хеша и распределение по корзинам. Даже если имена идут подряд (отсортированы), хеш‑функция (например, (h*31 + ord(ch)) % size) рассеивает их по разным индексам почти равномерно. Поэтому время вставки, поиска и удаления остаётся O(1) в среднем (с учётом разрешения коллизий цепочками), независимо от того, отсортированы данные или перемешаны.
В связном списке поиск элемента по имени требует последовательного просмотра узлов от головы до тех пор, пока не найдётся нужный или не достигнут конец. В худшем случае (элемент отсутствует или находится в конце) нужно проверить все N узлов. Сложность поиска O(N) в среднем. Никакая предобработка или порядок вставки не улучшают этот показатель, потому что структура не поддерживает эффективного индексирования. Даже если список отсортировать вручную (но у нас нет сортировки при вставке), поиск останется линейным, так как нельзя выполнить бинарный поиск без возможности прямого доступа по индексу.
· Связный список: удаление узла по имени требует линейного поиска (O(N)). Найденный узел исключается перенаправлением указателя предыдущего узла на следующий. Для удаления головы особая обработка. В моей реализации ll_delete возвращает новую голову. Время O(N).
· Хеш‑таблица: удаление сводится к вычислению индекса корзины (O(1)) и вызову ll_delete для связного списка этой корзины. Средняя длина цепочки N / bucket_count (обычно небольшая константа). Поэтому удаление в среднем O(1). В коде: buckets[idx] = ll_delete(buckets[idx], name).
· BST: удаление сложнее. Сначала ищется узел (O(log N) в сбалансированном дереве, O(N) в вырожденном). Если узел найден, то:
· Нет потомков просто удаляем.
· Один потомок заменяем удаляемый узел на потомка.
· Два потомка находим минимальный узел в правом поддереве (или максимальный в левом), копируем его данные в удаляемый узел, затем рекурсивно удаляем этот минимальный узел.
Моя реализация bst_delete рекурсивна, сложность совпадает со сложностью поиска. Для сбалансированного дерева O(log N), для вырожденного O(N).
· Связный список использовать, только если нужны частые вставки/удаления в начало/конец (например, очередь или стек) и поиск почти не требуется. Для телефонного справочника он не пригоден из-за линейного поиска.
· Хеш‑таблица идеальный выбор для задач, где важны быстрые вставка, поиск и удаление по ключу (O(1) в среднем) и не требуется получать записи в отсортированном порядке. Примеры: словари, кэши, таблицы символов, базы данных «ключ‑значение». Телефонный справочник с частыми поисками по имени отличное
· BST (особенно самобалансирующиеся варианты, такие как AVL или красно‑чёрное дерево) выбирают, когда нужны обе возможности: быстрый поиск (O(log N)) и возможность прохода по данным в отсортированном порядке без дополнительной сортировки. Также дерево может поддерживать операции поиска диапазона («все имена между A и B»). Но для простого справочника без требования сортировки на лету хеш‑таблица обычно предпочтительнее из-за константного времени. В данной реализации простой BST деградирует на упорядоченных данных, поэтому в реальной жизни используют сбалансированные деревья.
Вывод: Если нужен только доступ по ключу хеш‑таблица. Если нужен отсортированный вывод или диапазонные запросы сбалансированное дерево. Связный список для этой задачи неприменим.