Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат? |
Таблицы символов и деревья бинарного поиска
Реализации других функций АТД с помощью BST -ДЕРЕВА
Рекурсивные реализации из раздела 12.5 для основных операций найти, вставить и сортировать, использующие структуру бинарных деревьев, достаточно просты. В этом разделе мы рассмотрим реализации функций выбрать, объединить и удалить. Одна из них, выбрать, также допускает естественную рекурсивную реализацию, однако для других это может оказаться трудной задачей, приводящей к потере производительности. Операцию выбрать важно рассмотреть потому, что возможность эффективной поддержки операций выбрать и сортировать - одна из причин, по которой для многих приложений BST-деревья оказываются удобнее других структур. Хотя некоторые программисты стараются не использовать BST-деревья, чтобы не возиться с операцией удалить. В этом разделе рассматривается компактная реализация всех этих операций, использующая технику ротации к корню из раздела 12.8.
Обычно эти операции связаны с перемещением вниз по дереву; поэтому для случайных BST-деревьев можно ожидать, что затраты будут логарифмическими. Однако нельзя гарантировать, что BST-деревья останутся случайными после выполнения над ними многочисленных операций. В конце этого раздела мы еще вернемся к данному вопросу.
Для реализации операции выбрать можно использовать рекурсивную процедуру, аналогичную методу выборки на основе быстрой сортировки, описанному в "Быстрая сортировка" . Для отыскания в BST-дереве элемента с k-ым наименьшим ключом проверяется количество узлов в левом поддереве. Если там к узлов, возвращается корневой элемент. Иначе, если левое поддерево содержит более к узлов, в нем (рекурсивно) отыскивается к-й наименьший узел. Если неверно ни одно из этих условий, то левое поддерево содержит t элементов при t < k, и k-й наименьший элемент в BST-дереве является (k - t - 1)- ым наименьшим элементом в правом поддереве. Программа 12.14 является непосредственной реализацией этого метода. Как обычно, поскольку каждое выполнение функции завершается максимум одним рекурсивным вызовом, очевидна и нерекурсивная версия (см. упражнение 12.78).
Программа 12.14. Выборка с помощью BST-дерева
В этой процедуре предполагается, что каждый узел дерева содержит размер своего поддерева. Сравните эту программу с выборкой с помощью быстрой сортировки в массиве (программа 9.6).
private: Item selectR(link h, int k) { if (h == 0) return nullItem; int t = (h->l == 0) ? 0: h->l->N; if (t > k) return selectR(h->l, k); if (t < k) return selectR(h->r, k-t-1); return h->item; } public: Item select(int k) { return selectR(head, k); }
Реализация операции выбрать - основная алгоритмическая причина включения поля размера поддерева во все узлы BST-дерева. С помощью этого поля можно также обеспечить тривиальную " энергичную " реализацию операции подсчитать (возврат значения поля счетчика в корневом узле); в "Сбалансированные деревья" будет продемонстрировано еще одно применение. Недостатки присутствия поля счетчика заключаются в использовании дополнительной памяти для каждого узла и необходимости обновления поля каждой функцией, изменяющей дерево. Использование поля размера поддерева может не окупаться в некоторых приложениях, в которых основным операциями являются вставить и найти, но эта плата может оказаться незначительной, если в динамической таблице символов важна поддержка операции выбрать.
Эту реализацию операции выбрать можно преобразовать в операцию разбить (на части - partition), которая реорганизует дерево для помещения к-го наименьшего элемента в корень, используя точно такую же рекурсивную технику, которая использовалась для вставки в корень в разделе 12.8: если мы (рекурсивно) помещаем требуемый узел в корень одного из поддеревьев, его затем с помощью единственной ротации можно сделать корнем всего дерева. Программа 12.15 содержит реализацию этого метода. Подобно ротациям, разбиение не является операцией АТД, поскольку эта функция преобразует конкретное представление таблицы символов и должна быть прозрачной для клиентов. Скорее, это вспомогательная процедура, которую можно использовать для реализации операций АТД либо для повышения их эффективности. На рис. 12.17 приведен пример, показывающий, аналогично т рис. 12.14, что этот процесс эквивалентен спуску по пути от корня до требуемого узла дерева, а затем подъему обратно с выполнением ротаций для перемещения этого узла в корень.
Программа 12.15. Разбиение BST-дерева
Добавление ротаций после рекурсивных вызовов преобразует функцию выборки из программы 12.14 в функцию, которая помещает к-й наименьший узел BST-дерева в его корень.
void partR(link& h, int k) { int t = (h->l == 0) ? 0 : h->l->N; if (t > k) { partR(h->l, k); rotR(h); } if (t < k) { partR(h->r, k-t-1); rotL(h); } }
Здесь показан результат (внизу) разбиения BST-дерева (вверху) по медианному ключу; при этом выполняется (рекурсивно) ротация - точно так же, как и при вставке в корень.
Чтобы удалить из BST-дерева узел с заданным ключом, вначале необходимо проверить, находится ли он в одном из поддеревьев. Если да, мы заменяем это поддерево результатом (рекурсивного) удаления из него данного узла. Если удаляемый узел находится в корне, дерево заменяется результатом объединения двух поддеревьев в одно. Для выполнения такого объединения существует несколько возможностей. Один из возможных подходов проиллюстрирован на рис. 12.18, а реализация представлена в программе 12.16.
Здесь показан результат (внизу) удаления корня из BST-дерева (вверху). Вначале после удаления корневого узла остаются два поддерева (второй сверху рисунок). Затем мы разбиваем правое поддерево для помещения его наименьшего элемента в корень (третий сверху рисунок) - при этом левая ссылка указывает на пустое поддерево.
И, наконец, мы заменяем эту ссылку указателем на левое поддерево исходного дерева (внизу).