Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2179 / 0 | Длительность: 63:16:00
Лекция 12:

Таблицы символов и деревья бинарного поиска

Вставка в корень в деревьях бинарного поиска

В стандартной реализации BST-деревьев каждый вновь вставленный узел попадает куда-то в нижнюю часть дерева, заменяя некоторый внешний узел. Это не обязательное требование, а лишь следствие естественного алгоритма с использованием рекурсивных вставок. В этом разделе рассматривается другой метод вставки, при котором каждый новый элемент вставляется в корень, и поэтому недавно вставленные узлы находятся вблизи вершины дерева. Построенные таким образом деревья обладают рядом интересных свойств, но главная причина изучения этого метода в том, что он играет важную роль в двух усовершенствованных алгоритмах, которые будут рассмотрены в "Сбалансированные деревья" .

Предположим, что ключ вставляемого элемента больше ключа в корне. Тогда создание нового дерева можно начать с помещения нового элемента в новый корневой узел, со старым корнем в качестве левого поддерева и правым поддеревом старого корня в качестве правого поддерева. Однако правое поддерево может содержать и меньшие ключи, поэтому для завершения вставки потребуются дополнительные действия.

Аналогично, если ключ вставляемого элемента меньше ключа в корне и больше всех ключей в левом поддереве корня, можно также создать новое дерево с новым элементом, помещенным в корень, но если левое поддерево содержит какие-либо большие ключи, необходимы дополнительные действия. Перемещение всех узлов с меньшими ключами в левое поддерево и всех узлов с большими ключами в правое поддерево в общем случае кажется сложным преобразованием, поскольку узлы, которые должны быть перемещены, могут быть разбросаны по всему пути поиска для вставляемого узла.

К счастью, существует простое рекурсивное решение этой проблемы, основанное на ротации (rotation) - фундаментальном преобразовании деревьев. По существу, ротация позволяет менять местами роль корня и одного из его потомков, сохраняя BST-упорядоченность ключей в узлах дерева. Ротация вправо затрагивает корень и его левый дочерний узел (см. рис. 12.12). Эта ротация перемещает корень вправо, изменяя на обратное направление левой ссылки корня: перед ротацией она указывает от корня на левый дочерний узел, а после ротации - от старого левого потомка (нового корня) на старый корень (правый дочерний узел нового корня). Основная часть, которая обеспечивает работу ротации - копирование правой ссылки левого потомка, чтобы она стала левой ссылкой старого корня. Эта ссылка указывает на все узлы с ключами между двумя узлами, участвующими в ротации. После этого нужно изменить ссылку на старый корень так, чтобы она указывала на новый корень. Описание ротации влево аналогично вышеприведенному, только везде слово " правый " должно быть заменено на " левый " и наоборот (см. рис. 12.13).

 Ротация вправо в BST-дереве

Рис. 12.12. Ротация вправо в BST-дереве

На этой диаграмме показан результат (внизу) ротации вправо в узле S BST-дерева, приведенного вверху. Узел, содержащий ключ S, перемещается в дереве вниз и становится правым дочерним узлом своего прежнего левого дочернего узла.

Для выполнения ротации мы получаем ссылку на новый корень E из левой ссылки узла S, копируем в левую ссылку S правую ссылку E, в правую ссылку E - указатель на S и заменяем ссылку из A на S указателем на E. Эффект ротации заключается в перемещении узла E и его левого поддерева на один уровень вверх, а узла S и его правого поддерева - на один уровень вниз. Остальная часть дерева остается неизменной.

 Ротация влево в BST-дереве

Рис. 12.13. Ротация влево в BST-дереве

На этой диаграмме показан результат (внизу) ротации вправо в узле A BST-дерева, приведенного вверху. Узел, содержащий ключ A, перемещается в дереве вниз и становится левым дочерним узлом своего прежнего правого дочернего узла.

Для выполнения ротации мы получаем ссылку на новый корень E из правой ссылки узла A, копируем в правую ссылку A левую ссылку E, в левую ссылку E - указатель на A и заменяем ссылку на A (верхняя ссылка дерева) указателем на E.

Ротация - это локальное изменение, затрагивающее только три ссылки и два узла; оно позволяет перемещать узлы по деревьям без изменения глобальных свойств упорядоченности, которые и делают BST-дерево полезной для поиска структурой (см. программу 12.12). Ротации применяются для перемещения конкретных узлов по дереву и предотвращения разба-лансировки деревьев. В разделе 12.9 с помощью ротаций будут реализованы операции удалить, объединить и другие операции АТД; в "Сбалансированные деревья" они будут применяться для построения деревьев, дающих почти оптимальную производительность.

Программа 12.12. Ротации в BST-деревьях

Эти две симметричные процедуры выполняют операцию ротация в BST-дереве. Ротация вправо делает старый корень правым поддеревом нового корня (старого левого поддерева корня); ротация влево делает старый корень левым поддеревом нового корня (старого правого поддерева корня). Для реализаций, где в узлах содержится поле счетчика (например, для поддержки операции выбрать в "Хеширование" ), необходимо также пересчитывать значений этих полей для участвующих в ротации узлах (см. упражнение 12.75).

void rotR(link& h)
  { link x = h->l; h->l = x->r; x->r = h; h = x; }
void rotL(link& h)
  { link x = h->r; h->r = x->l; x->l = h; h = x; }
      

Операции ротации обеспечивают простую рекурсивную реализацию вставки в корень: необходимо рекурсивно вставить новый элемент в соответствующее поддерево (оставив его, по завершении рекурсивной операции, в корне этого дерева), а затем выполнить ротацию, чтобы сделать этот элемент корнем основного дерева. На рис. 12.14 приведен пример, а программа 12.13 является непосредственной реализацией данного метода.

 Вставка в корень BST-дерева

Рис. 12.14. Вставка в корень BST-дерева

Здесь показан результат вставки узла G в BST-дерево, приведенное на верхнем рисунке, с (рекурсивной) ротацией после вставки, которая перемещает вставленный узел G в корень. Этот процесс эквивалентен вставке G с последующим выполнением последовательности ротаций для перемещения его в корень.

Программа 12.13. Вставка в корень BST-дерева

С помощью функций ротации из программы 12.12 реализация рекурсивной функции, которая вставляет новый узел в корень BST-дерева, очевидна: необходимо вставить новый элемент в корень соответствующего поддерева, а затем выполнить соответствующую ротацию, чтобы перенести его в корень основного дерева.

        private:
void insertT(link& h, Item x)
  { if (h == 0) { h = new node(x); return; }
    if (x.key() < h->item.key())
      { insertT(h->l, x); rotR(h); }
    else
      { insertT(h->r, x); rotL(h); }
  }
        public:
void insert(Item item)
  { insertT(head, item); }
      

Эта программа представляет собой убедительный пример больших возможностей рекурсии: любой читатель, которого это не убеждает, может попытаться выполнить упражнение 12.76.

На рис. 12.15 и рис. 12.16показано создание BST-дерева вставкой последовательности ключей в первоначально пустое дерево с использованием метода вставки в корень. Если последовательность ключей случайна, созданное таким образом BST-дерево обладает в точности теми же стохастическими свойствами, что и BST-дерево, созданное стандартным методом. Например, леммы 12.6 и 12.7 справедливы и для BST-деревьев, построенных вставками в корень. На практике преимущество метода вставки в корень состоит в том, что недавно вставленные ключи располагаются вблизи вершины. Следовательно, затраты на удачный поиск недавно вставленных ключей будут, скорее всего, ниже, чем при стандартном методе. Это важное свойство, поскольку многим приложениям присуща именно такая динамическая смесь операций найти и вставить. Таблица символов может содержать довольно большое количество элементов, но значительная часть поисков может относиться к самым последним вставленным элементам. Например, в системе обработки коммерческих транзакций активные транзакции могут оставаться вблизи вершины и обрабатываться быстро без обращения к старым потерянным транзакциям. Метод вставки в корень автоматически придает структуре данных это и аналогичные свойства.

 Построение BST-дерева вставками в корень

Рис. 12.15. Построение BST-дерева вставками в корень

Эта последовательность демонстрирует результат вставки ключей A S E R C H I в корень первоначально пустого BST-дерева. После вставки в корень каждого нового узла изменяются ссылки, расположенные вдоль его пути поиска, чтобы получилось правильное BST-дерево.

 Построение BST-дерева вставками в корень (продолжение)

Рис. 12.16. Построение BST-дерева вставками в корень (продолжение)

Эта последовательность демонстрирует вставку ключей N G X M P L в BST-дерево, построение которого начато на рис. 12.15.

Если изменить еще и функцию найти, чтобы при успешном поиске она помещала найденный узел в корень, то получится метод самоорганизующегося поиска (см. упражнение 12.28), который сдвигает часто посещаемые узлы к вершине дерева. В главе 13 "Сбалансированные деревья" будет показано систематическое применение этой идеи при реализации таблицы символов, обладающей гарантированно быстрой производительностью.

Как и для ряда других методов, упомянутых в этой главе, для реальных приложений трудно точно сравнить производительность метода вставки в корень со стандартным методом вставки, поскольку производительность настолько зависит от смеси различных операций с таблицей символов, что ее трудно проанализировать аналитически. Невозможность проанализировать алгоритм не обязательно должна удерживать от использования вставки в корень, когда известно, что основная масса поисков будет связана с недавно вставленными данными, однако мы всегда пытаемся найти гарантированные показатели производительности. Методы построения BST-деревьев, которые могут предоставить такие гарантии, являются основной темой "Сбалансированные деревья" .

Упражнения

12.73. Нарисуйте BST-дерево, образованное вставками элементов с ключами E A S Y Q U E S T I O N в корень первоначально пустого дерева.

12.74. Приведите последовательность из 10 ключей (используя буквы от A до J), которая требует максимального количества сравнений при создании дерева вставками в корень первоначально пустого дерева. Укажите количество используемых сравнений.

12.75. Добавьте в программу 12.12 код, необходимый для корректного изменения полей счетчиков, которые должны изменяться после ротации.

12.76. Разработайте нерекурсивную реализацию вставки в корень BST-дерева (см. программу 12.13).

12.77. Эмпирически определите среднее значение и среднеквадратичное отклонение количества сравнений, выполняемых при успешных и неудачных поисках в BST-дереве, которое построено вставками N случайных ключей в первоначально пустое дерево. После построения в этом дереве выполняется последовательность N произвольных поисков N/10 самых последних вставленных ключей для N = 103, 104, 105 и 106 . Проведите эксперименты и для стандартного метода вставки, и для метода вставки в корень, а затем сравните полученные результаты.

Бактыгуль Асаинова
Бактыгуль Асаинова

Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат?

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?