
- •3.7. Древовидные структуры для задачи объединить–найти
- •Алгоритм 3.3. Алгоритм быстрого объединения непересекающихся множеств
- •VОтец [I]
- •3.8. Приложения и обобщения алгоритма объединить–найти
- •Приложение 2. Задача определения глубины
- •3.9. Схемы сбалансированных деревьев
- •3.10. Словари и очереди с приоритетами
- •Алгоритм 3.4. Вставка нового элемента в 2-3-дерево
- •3.11. Сливаемые деревья
- •3.12. Сцепляемые очереди
- •3.13. Разбиение
Приложение 2. Задача определения глубины
Дана последовательность операций двух типов: связать(v, r) и НАЙТИ_ГЛУБИНУ(v). Начнемся неориентированных корневых деревьев, каждое из которых состоит из единственного узла i, 1 i n. Операция связать(v, r), где r – корень дерева, а v – узел другого дерева, делает корень r сыном узла v. Условия о том, что v и r принадлежат различным деревьям и что r – корень, гарантируют, что получаемый в результате граф также будет лесом. Операция НАЙТИ_глубину(v) состоит в том, чтобы найти и напечатать глубину узла v в текущий момент.
Если при работе с лесом использовать обычное представление в виде списков смежностей и вычислять глубину узлов очевидным образом, то сложность алгоритма будет O(п2). Вместо этого мы представим исходный лес другим лесом, который будем называть D-лесом. Он нужен нам только для того, чтобы можно было быстро вычислять глубины. Каждому узлу в D-лесу приписывается целочисленный вес так, чтобы сумма весов вдоль пути в D-лесу от узла и к корню равнялась глубине узла v в исходном лесу. Для каждого дерева в D-лесу хранится счетчик числа узлов в этом дереве.
Вначале D-лес состоит из п деревьев, каждое из которых содержит единственный узел, соответствующий целому числу i, 1 i n. Начальный вес каждого узла равен нулю.
Для выполнения операции НАЙТИ_глубину(v) мы проходим путь из узла v в корень r. Пусть v1, v2, . . ., vk – узлы на этом пути (v1 = v, vk = r). Тогда
Кроме
того, применяем сжатие путей. Каждый
узел vi
1
i
k–2,
делается
сыном корня r.
Чтобы сохранялось сформулированное
выше свойство
весов, новый ВЕС узла vi
должен равняться
для 1
i
k.
Так как новые веса можно вычислить за
время O(k),
то операция
НАЙТИ_ГЛУБИНУ имеет ту же временную
сложность, что и операция НАЙТИ.
Чтобы выполнить операцию связать(v, r), соединим деревья, содержащие узлы v и r, снова вливая меньшее дерево в большее. Пусть Тv и Тr – деревья в D-лесу, содержащие v и r соответственно, а v' и r' – их корни. Деревья в D-лесу не обязательно изоморфны деревьям в исходном лесу, так что, в частности, r может не быть корнем для Тr. Пусть СЧЕТ(T) обозначает число узлов в дереве Т. Рассмотрим отдельно два случая.
Случай 1. СЧЕТ(Tr) CЧЕТ(Tv). Делаем r' сыном узла v'. Мы должны также скорректировать вес старого корня r' дерева Тr так, чтобы глубина каждого узла w в Тr правильно вычислялась при прохождении пути из w в v' в объединенном дереве. Чтобы сделать это, выполняем операцию найти_глубину(v), а затем
ВЕС[г'] ВЕС[r'] – ВЕС[v'] + ГЛУБИНА(v) + 1.
Таким образом, глубина каждого узла в Тr эффективно увеличена на глубину узла v плюс 1. Наконец, полагаем счетчик числа узлов объединенного дерева равным сумме счетчиков для Тr и Тv.
Случай 2. СЧЕТ(Tv) < СЧЕТ(Tr). Здесь вычисляется ГЛУБИНА(v), преобразуется узел v' в сына узла r' и
ВЕС[r'] ВЕС [r'] + глубина(v) + 1;
BEC[v'] BEC[v'] – BEC[r'];
СЧЕТ(Тr) – СЧЕТ(Tv) + СЧЕТ(Tr).
Итак, O(п) операций СВЯЗАТЬ и НАЙТИ_ГЛУБИНУ можно выполнить за время O(nG(n)).
Приложение 3. Эквивалентность конечных автоматов
Детерминированным конечным автоматом называется машина, распознающая цепочки символов. Она имеет входную ленту, разбитую на клетки, головку на входной ленте (входную головку) и управляющее устройство с конечным числом состояний (рис. 3.24). Конечный автомат М можно представить в виде пятерки (S, I, , so, F), где
1) S – множество состояний управляющего устройства,
2) I – входной алфавит (каждая клетка входной ленты содержит символ из I),
3) – отображение из S x I в S (если (s, a) = s то всякий раз, когда М находится в состоянии s, а входная головка обозревает символ а, М сдвигает входную головку вправо и переходит в состояние s'),
4) sо – выделенное состояние в S, называемое начальным,
5) F – подмножество в S, называемое множеством допускающих (или заключительных) состояний.
Пусть I* – множество цепочек (слов) конечной длины, состоящих из символов алфавита I. В I* включается и пустая цепочка . Продолжим до отображения из S x I* в S:
1) (s, ) = s,
2) (s, ха) = ((s, x), а) для всех х I* и а I.
Входная цепочка х допускается автоматом М, если (sо, х) F. Языком L(M), допускаемым автоматом М, называется множество всех цепочек, допускаемых М. Более подробное введение в теорию конечных автоматов изложено в разд. 9.1.
Два состояния s1 и s2 считаются эквивалентными, если для каждого х I* состояние (s1, х) будет допускающим тогда и только тогда, когда (s2, х) – допускающее состояние.
Два конечных автомата M1 и М2 считаются эквивалентными, если L(M1)=L(М2). Мы покажем здесь, что с помощью алгоритма ОБЪЕДИНИТЬ – НАЙТИ можно распознать эквивалентность двух конечных автоматов M1 = (S1, I, 1, s1, F1) и M2=(S2, I, 2, s2 F2) за O(nG(n)) шагов, где n = ||S1|| + ||S2||.
Отношение эквивалентности двух состояний обладает важным свойством: если два состояния s и s' эквивалентны, то для всех входных символов а состояния (s, а) и (s, а) также эквивалентны. Кроме того, благодаря наличию пустой цепочки, никакое допускающее состояние не может оказаться эквивалентным недопускающему. Таким образом, если допустить, что начальные состояния s1 и s2 автоматов М1 и М2 эквивалентны, то можно вывести другие пары эквивалентных состояний. Если в одну из таких пар попадет допускающее состояние вместе с недопускающим, то s1 и s2 были неэквивалентными. Если же это не произойдет, то верно обратное
begin
СПИСОК(s1, s2);
НАБОР;
for sS1 S2 do добавить {s} в НАБОР;
comment Мы только что образовали исходные множества для каждого состояния из S1 S2;
while есть пара (s, s'), входящая в СПИСОК do
begin
удалить (s, s') из множества СПИСОК; –
пусть A и A' обозначают НАЙТИ (s) и НАЙТИ (s') соответственно;
if A A' then
begin
ОБЪЕДИНИТЬ (A, A', A);
for a I do
добавить ((s, a), (s', а)) в СПИСОК
end
end
end
Рис. 3.25. Алгоритм для нахождения множеств эквивалентных состояний в предположении, что s1 и s2 эквивалентны.
Чтобы узнать, эквивалентны ли два конечных автомата М1 = (s1, I, 1, s1, F1) и М2 = (s2, I, 2, s2, F2), мы поступаем так:
1. С помощью программы на рис. 3.25 находим все множества состояний, которые должны быть эквивалентными, если эквивалентны s1 и s2. СПИСОК содержит такие пары состояний (s, s'), что s и s' оказались эквивалентными, а следующие за ними состояния ((s, a), (s, a) еще не рассматривались. Вначале СПИСОК содержит только пару (s1, s2). Чтобы найти множества эквивалентных состояний, программа применяет алгоритм объединения непересекающихся множеств. НАБОР представляет некоторое семейство множеств. Вначале каждое состояние из S1S2 образует одноэлементное множество. (Без потери общности можно считать, что множества S1 и S2 не пересекаются.) Затем всякий раз, когда s и s' оказываются эквивалентными, содержащие их множества A и A', входящие в НАБОР, сливаются, и новое множество получает имя А.
2. После выполнения этой программы множества, входящие в НАБОР, представляют разбиение множества S1S2 на блоки состояний, которые должны быть эквивалентными. М1 и М2 эквивалентны тогда и только тогда, когда никакой блок не содержит допускающего состояния вместе с недопускающим.
Время работы этого алгоритма (как функция от числа состояний n = ||S1|| + ||S2|| определяется в основном работой алгоритма объединения множеств. Операций ОБЪЕДИНИТЬ может быть не более п–1, поскольку каждая такая операция уменьшает на единицу число множеств, входящих в НАБОР, а вначале было только п множеств. Число операций НАЙТИ пропорционально числу пар, помещенных в СПИСОК. Это число не больше n||I||, так как всякая пара, кроме начальной (s1, s2), попадает в СПИСОК только после операции ОБЪЕДИНИТЬ. Таким образом, при постоянном размере входного алфавита на распознавание эквивалентности конечных автоматов М1 и М2 тратится время O(nG (n)).