Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конец главы 3.doc
Скачиваний:
13
Добавлен:
05.11.2018
Размер:
926.21 Кб
Скачать

28

Пример 3.6. Пусть n = 8 и у нас есть набор из трех множеств {1, 3, 5, 7}, {2, 4, 8} и {6} с внешними именами 1, 2 и 3 соответственно. Структуры данных для этих трех множеств показаны на рис. 3.13, где 2,3 и 1 – внутренние имена для 1,2 и 3 соответственно. 

Операция НАЙТИ (i) выполняется, как и раньше, обращением к R[i] для установления внутреннего имени множества, содержащего элемент i в данный момент. Затем ВНЕШ_ИМЯ[R[(i)]] дает настоящее имя множества, которому принадлежит i.

Операцию объединения вида ОБЪЕДИНИТЬ (I, J, К) выполняем следующим образом. (Номера строк относятся к рис. 3.14.)

1. Определяем внутренние имена для множеств I и J (строки 1,2).

2. Сравниваем относительные размеры множеств I и J, справляясь в массиве РАЗМЕР (строки 3, 4).

3. Проходим список элементов меньшего множества и изменяем соответствующие компоненты в массиве R на внутреннее имя большего множества (строки 5–9).

4. Вливаем меньшее множество в большее, добавляя список элементов меньшего множества к началу списка для большего множества (строки 10–12).

5. Присваиваем полученному множеству внешнее имя К (строки 13, 14).

Вливая меньшее множество в большее, мы делаем время выполнения операции ОБЪЕДИНИТЬ пропорциональным мощности меньшего множества. Все детали приведены в процедуре на рис. 3.14.

Пример 3.7. После выполнения операции ОБЪЕДИНИТЬ (1, 2, 4) структура данных рис. 3.13 превратится в структуру данных, изображенную на рис. 3.15. 

Теорема 3.3. С помощью алгоритма рис. 3.14 можно выполнить п - 1 (максимально возможное число) операций ОБЪЕДИНИТЬ за O (п log n) шагов.

Доказательство. За каждое выполнение операции ОБЪЕДИНИТЬ будем налагать на перемещаемые элементы одинаковые штрафы, в сумме равные сложности этого выполнения. Так как сложность пропорциональна числу перемещаемых элементов, то величина штрафа, налагаемого на один элемент, будет всегда одна и та же. Основное здесь – это заметить, что всякий раз, когда элемент перемещается из списка, он оказывается в списке, по крайней мере в два раза длиннее прежнего. Поэтому никакой элемент нельзя переместить более чем log n раз и, значит, суммарный штраф, налагаемый на один элемент, составляет O (log n).

Общая сложность получается суммированием штрафов, наложенных на отдельные элементы. Таким образом, общая сложность есть O(п log n) .

Из теоремы 3.3 вытекает, что если выполняется т операций НАЙТИ и до п - 1 операций ОБЪЕДИНИТЬ, то тратится время O (МАХ (m, n log п)). Если т имеет порядок п log п или больше, то этот алгоритм действительно оптимален с точностью до постоянного множителя. Однако во многих ситуациях т есть O(п), а в таком случае, как мы увидим в следующем разделе, можно добиться лучшего времени, нежели O (МАХ (m, п log п)),

3.7. Древовидные структуры для задачи объединить–найти

В предыдущем разделе мы познакомились со структурой данных для задачи ОБЪЕДИНИТЬ – НАЙТИ, позволяющей выполнить п - 1 операций ОБЪЕДИНИТЬ и O(п log п) операций НАЙТИ за время O(n log n). В данном разделе будет описана структура данных, которая состоит из. деревьев, образующих лес, и предназначена для представления набора множеств. Эта структура данных позволит выполнить O(п) операций ОБЪЕДИНИТЬ и НАЙТИ за почти линейное время.

Допустим, что каждое множество А представлено корневым нeориентированным деревом ТA, узлам которого поставлены в соответствие элементы из А. Корню этого дерева приписано имя самого множества. Операцию ОБЪЕДИНИТЬ (A, B, C) можно выполнить, преобразуя корень дерева ТA в сына корня дерева ТB и заменяя имя в корне дерева TB на С. Операцию НАЙТИ (i) можно выполнить, определяя положение узла, представляющего элемент i в некотором дереве Т из леса, и проходя путь из этого узла в корень дерева T, где помещено имя множества, содержащего i.

В такой схеме сложность слияния двух деревьев равна постоянной. Однако сложность выполнения операции НАЙТИ (i) имеет порядок длины пути из узла i в корень. Эта длина может быть п–1. Поэтому сложность выполнения п–1 операций ОБЪЕДИНИТЬ, за которыми идут п операций НАЙТИ, может достигать O(п2). Например, вычислим сложность выполнения последовательности операций

ОБЪЕДИНИТЬ (1, 2, 2)

ОБЪЕДИНИТЬ (2, 3, 3)

.

.

.

ОБЪЕДИНИТЬ(n - 1, п, п)

НАЙТИ(1)

НАЙТИ(2)

.

.

.

НАЙТИ(n)

Выполнение п - 1 операций ОБЪЕДИНИТЬ приводит к дереву, изображенному на рис. 3.16. Сложность выполнения п операций НАЙТИ пропорциональна

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

Лемма 3.1. Если при выполнении каждой операции ОБЪЕДИНИТЬ корень дерева с меньшим числом узлов (при равенстве узлов берется любое дерево) преобразуется в сына корня дерева с большим числом узлов, то высота дерева в лесу может достичь значения h, только если оно имеет не менее 2h узлов.

Доказательство. Доказательство проведем индукцией по h. Для h = 0 утверждение верно, поскольку каждое дерево имеет по крайней мере, один узел. Допустим, что утверждение верно для всех значений параметра, меньших . Пусть Т – дерево высоты h с наи-

Рис. 3.16. Дерево после выполнения операций ОБЪЕДИНИТЬ.

меньшим числом узлов. Тогда оно должно было получиться в результате слияния деревьев T1 и T2, где T2 дерево высоты i - 1 и у него узлов не больше, чем у Т2. По предположению индукции T1 имеет не менее 2h-1 узлов, и, значит, Т2 имеет не менее 2h-1 узлов, откуда следует, что Т имеет не менее 2h узлов. 

Оценим время выполнения (в худшем случае) последовательности из п операций ОБЪЕДИНИТЬ и НАЙТИ, если пользоваться структурой данных в виде леса, модифицированной так, что в операции ОБЪЕДИНИТЬ корень меньшего дерева становится сыном корня большего дерева. Никакое дерево не может иметь высоту, большую log п. Следовательно, сложность выполнения O(п) операций ОБЪЕДИНИТЬ и НАЙТИ не превосходит O(п log п). Эта граница точна в том смысле, что существуют последовательности из п операций, занимающие время, пропорциональное п log п.

Изложим еще одну модификацию этого алгоритма, называемую сжатием путей. Поскольку в общей сложности преобладает сложность операций НАЙТИ, попробуем уменьшить ее. Всякий раз, когда выполняется операция НАЙТИ (i), мы проходим путь от узла i до корня r. Пусть – узлы на этом пути. Тогда каждый из узлов делается сыном корня. На рис. 3.17, б отражено влияние операции НАЙТИ (i) на дерево, приведенное на рис. 3.17, а

Вся процедура слияния деревьев для задачи ОБЪЕДИНИТЬ – НАЙТИ, включая сжатие путей, выражена в следующем алгоритме.