7. 1. Пирамида – binary heap – объект-массив; почти полное бинарное дерево: • узел дерева – элемент массива • на всех уровнях (кроме последнего) дерево заполнено полностью • последний уровень заполняется слева направо 1
7. 1. Пирамида Атрибуты массива – пирамиды A: • length(A) – количество элементов массива • heap_size(A) – количество элементов пирамиды, содержащихся в массиве heap_size(A) length(A) 2
7. 1. Пирамида • Элементы массива A 1, A 2, … • A 1 – корень дерева • Если Ai – промежуточный узел дерева, то для него: – индекс родительского узла parent(i) = i / 2 – индекс левого дочернего узла left(i) = 2 i – индекс правого дочернего узла right(i) = 2 i + 1 3
7. 2. Свойства пирамид Два вида пирамид: • невозрастающие – max-heap property: Aparent(i) Ai пирамидальные сортировки • неубывающие – min-heap property: Aparent(i) Ai очереди с приоритетами 4
7. 3. Пример пирамиды left(i) = 2 i right(i) = 2 i + 1 1 2 3 16 14 10 4 8 5 7 6 7 8 9 3 2 9 4 10 1 5
7. 3. Пример пирамиды Представление в виде дерева 1 16 2 3 14 10 4 5 6 7 8 7 9 3 8 9 10 2 4 1 6
7. 4. Max_Heapify(A, i) Случай 1 Поддержка свойства пирамиды Предположения: • бинарные деревья с корнями left(i) и right(i) – невозрастающие пирамиды • элемент Ai может нарушить свойство пирамиды Время работы с узлом, расположенным на высоте h – Ο(h) 7
7. 4. Max_Heapify(A, i) l = left(i) r = right(i) largest = индекс максимального из трех элементов: Ai, Al, Ar, при условии, что l heap_size(A) и r heap_size(A) if largest ≠ i { поменять местами Ai и Alargest Max_Heapify(A, largest) } 8
7. 5. Пример A = <27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0> Max_Heapify(A, 3) 27 17 1 2 3 3 16 13 10 4 5 6 1 5 7 12 7 8 9 10 4 8 9 0 11 12 13 14 9
7. 5. Пример A = <27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0> Max_Heapify(A, 3) 27 17 10 16 13 1 2 3 4 5 3 1 5 7 12 6 7 8 9 10 4 8 9 0 11 12 13 14 10
7. 5. Пример A = <27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0> Max_Heapify(A, 3) 27 17 10 16 13 1 2 3 4 5 9 1 5 7 12 6 7 8 9 10 4 8 3 0 11 12 13 14 11
7. 6. Создание пирамиды Дан массив А = <A 1, A 2, …, An>, n = length(A) Элементы массива Aj+1, Aj+2, …, An, где j = n / 2 – листья Построение пирамиды – от листьев к корню 12
7. 7. Build_Max_Heap(A) heap_size(A) = length(A) i = n/2 while i > 0 { Max_Heapify(A, i) i=i– 1 } Время работы алгоритма – Ο(n log n); более точная оценка – Ο(n) 13
7. 8. Иллюстрация i=5 1 A 2 4 1 3 3 4 5 6 2 16 9 7 8 10 14 9 10 8 7 14
7. 8. Иллюстрация i=4 1 A 4 1 1 A 2 2 4 1 3 3 4 5 6 2 16 9 4 5 6 7 9 10 14 16 7 8 9 10 8 7 8 9 10 2 8 7 10 14 15
7. 8. Иллюстрация i=3 1 A 4 1 1 A 2 3 4 1 10 14 16 3 4 5 6 7 8 9 10 14 16 9 10 2 8 7 4 6 7 8 9 10 9 3 2 8 7 5 16
7. 8. Иллюстрация i=2 1 A 3 4 1 1 A 2 4 2 6 7 8 9 10 10 14 16 9 3 2 8 7 3 5 6 7 8 9 10 4 16 10 14 1 9 3 2 8 7 4 5 17
7. 8. Иллюстрация i=1 1 4 1 A 2 A 16 3 4 5 6 7 8 9 10 16 10 14 7 9 3 2 8 1 2 3 5 6 7 8 9 10 4 10 14 7 9 3 2 8 1 4 18
7. 8. Иллюстрация i=1 1 2 3 A 16 14 10 4 5 6 7 8 9 10 4 7 9 3 2 8 1 4 5 6 7 8 9 10 8 7 9 3 2 4 1 19
7. 9. Пирамидальная сортировка Шаг 1: построить невозрастающую пирамиду Шаг 2: сортировка • поменять местами первый и последний элементы пирамиды • уменьшить размер пирамиды • скорректировать свойство пирамиды для первого узла 20
7. 10. Heap_Sort(A) Build_Max_Heap(A) i = length(A) while i 2 { поменять местами A 1 и Ai heap_size(A) = heap_size(A) – 1 Max_Heapify(A, 1) i=i– 1 } 21
7. 11. Иллюстрация сортировки Построение пирамиды 1 2 3 16 1 14 14 10 1 8 1 14 10 1 2 8 3 10 9 1 4 5 6 7 8 9 10 8 4 1 7 9 3 2 4 1 1 16 4 5 6 7 8 9 10 9 1 3 2 1 14 16 4 7 22
7. 12. Очереди с приоритетами Два вида: невозрастающие и неубывающие Очередь с приоритетами (priority queue) – структура данных, предназначенная для обслуживания множества S, с каждым элементом которого связано определенное значение, называющееся ключом (key), или приоритетом 23
7. 13. Операции Для невозрастающей очереди с приоритетами: • Insert(S, x) – вставить элемент x в множество S • Maximum(S) – вернуть элемент множества S с наибольшим ключом • Extract_Max(S) – вернуть элемент с наибольшим ключом, удалив его из множества S 24
7. 13. Операции • Increase_Key(S, x, k) – увеличить значение ключа, соответствующего элементу х, путем его замены ключом со значением k (предполагается, что величина k не меньше текущего ключа элемента х) 25
7. 14. Maximum(A) Heap_Maximum(A) Возвращает A 1 Время работы – (1) 26
7. 15. Extract_Maximum(A) if очередь пуста отказ Извлечь из пирамиды A 1: res = A 1 Поместить в первую позицию пирамиды ее последний элемент Уменьшить размер пирамиды на 1 Восстановить свойства пирамиды для первого узла Время работы Ο(log n) 27
7. 16. Increase_Key(A, i, key) if key < Ai отказ: новый ключ меньше текущего Обновить ключ: Ai = key Вставить обновленный элемент в соответствующую позицию: while i > 1 и Aparent(i) < Ai { обменять значения Ai и Aparent(i) i = parent(i) } 28
7. 17. Пример Изменить ключ элемента с индексом 9 на 15 1 2 3 4 16 15 10 15 14 8 14 5 7 6 7 8 9 10 9 3 2 4 15 8 1 Время работы – Ο(log n) 29
7. 18. Insert(A, key) Вставить новый лист с ключом – : heap_size(A) = heap_size(A) + 1 Aheap_size(A) = – Увеличить значение ключа: Increase_Key(A, heap_size(A), key) Время вставки – Ο(log n) 30
7. 19. Skip Lists – списки с пропусками: вероятностная структура данных; альтернатива сбалансированным деревьям Предложены William Pugh (1990 г. ) В основе – обычный упорядоченный односвязный список 31
7. 20. Общий подход Обычный упорядоченный список 3 6 7 9 12 17 19 21 Ø Поиск в наихудшем случае: n элементов Дополнительные указатели: 3 6 7 9 12 17 19 21 Ø Поиск – n/2 + 1 элемент 32
7. 20. Общий подход 3 6 9 7 21 Ø 21 12 17 Ø 19 Поиск – n/4 + 2 элемента 3 6 9 7 12 17 19 Поиск – log 2 n 33
7. 21. Определения Элемент списка, имеющий k указателей – элемент уровня k Если каждый 2 i-ый элемент имеет 2 i указателей: 50% элементов – элементы уровня 1 (n 1) 25% элементов – элементы уровня 2 (n 2 = n 1 / 2) 12, 5% элементов – элементы уровня 3 (n 3 = n 2 / 2) 34
7. 22. Skip Lists Уровни элементов списка выбираются случайным образом Элемент списка уровня j указывает на следующий элемент этого же уровня (не обязательно это 2 j-ый элемент списка) Уровень элемента списка выбирается случайно при вставке элемента и не изменяется в дальнейшем 35
7. 22. Skip Lists 6 3 7 9 Ø 25 12 19 21 Операции вставки и удаления потребуют только локальные модификации (переопределение указателей) 36
7. 23. Предположения В общем случае p – доля элементов i-го уровня, определяющая элементы (i +1)-го уровня Обычно p = ½ или p = ¼ Уровень списка – максимальный уровень вершины в списке Уровень элемента списка ограничен некоторым значением Max. Level 37
7. 23. Предположения Заголовок Skip Lists – содержит уровень списка и массив из Max. Level указателей Указатели заголовка списка для уровней, превышающих текущий уровень списка, указывают на специальный элемент EList Уровень элемента генерируется случайным образом независимо от количества элементов в Skip List 38
7. 23. Предположения Определение уровня элемента В идеале начать поиск на уровне L, где ожидается 1/p элементов: L(n) = log 1/pn 39
7. 23. Предположения Определение Max. Level = L(N), N – максимально допустимое количество элементов в Skip Lists Для p = ½ выбор Max. Level = 16 позволяет разместить в Skip Lists до 216 элементов 40
7. 24. Структура элемента списка key – ключ элемента сопутствующая информация level – количество указателей forwards[ ] – массив из Max. Level указателей Элемент EList – специальный элемент списка с максимальным значением ключа 41
7. 25. Задание Skip Lists struct Item { … }; – элемент списка в Skip Lists struct Skip. Lists { int level; – текущий уровень списка struct Item *header; – указатель на головной элемент }; struct Skip. Lists *list; – указатель на Skip Lists 42
7. 26. Генерация уровня level = 1 while random() < p и level < Max. Level level = level + 1 random() – возвращает случайное число из [0 – 1): ((double) rand() – 1) / RAND_MAX 43
7. 27. Алгоритм поиска list – указатель на Skip Lists k – искомый ключ x = list->header – указатель на первый элемент верхнего уровня цикл по i от list->level – 1 до 0 { while x->forward[i]->key < k x = x->forward[i] } 44
7. 27. Алгоритм поиска Вышли на список элементов 1 -го уровня x = x->forward[0] if x->key = k успех: x else отказ 45
7. 28. Пример поиска x 6 3 7 9 Ø 25 12 19 21 Найти 19 Найти 5 46
7. 29. Алгоритм вставки list – указатель на Skip Lists k – новое значение update[ ] – локальный массив из Max. Level указателей x = list->header Поиск позиции в Skip Lists для вставки нового элемента 47
7. 29. Алгоритм вставки цикл по i от list->level – 1 до 0 { while x->forward[i]->key < k x = x->forward[i] update[i] = x – позиция предшествующего элемента i-го уровня } x = x->forward[0] – элемент, перед которым нужно вставить новый 48
7. 29. Алгоритм вставки if x->key = k отказ level = уровень нового элемента (случайный) if list->level < level { добавить в элементы update[i] значения list ->header list->level = level } 49
7. 29. Алгоритм вставки x = новый элемент списка уровня level модифицировать указатели: для всех i от 0 до level – 1 { x->forward[i] = update[i]->forward[i] = x } 50
7. 30. Пример вставки x Вставить элемент 17 6 3 7 9 Ø 25 12 19 21 17 update 51
7. 31. Алгоритм удаления list – указатель на Skip Lists k – удаляемое значение update[ ] – локальный массив из Max. Level указателей x = list->header Поиск в Skip Lists удаляемого элемента 52
7. 31. Алгоритм удаления цикл по i от list->level – 1 до 0 { while x->forward[i]->key < k x = x->forward[i] update[i] = x – позиция предшествующего элемента i-го уровня } x = x->forward[0] – удаляемый элемент 53
7. 31. Алгоритм удаления if x->key = k { модифицируем указатели: i=0 while i < level и update[i]->forward[i] = x { update[i]->forward[i] = x->forward[i] i=i+1 } удалить x 54
7. 31. Алгоритм удаления уменьшить, при необходимости, уровень списка: while list->level> 1 и list->header->forwards[list->level] = EList list->level = list->level – 1 55
7. 32. Пример удаления x Удалить элемент 19 6 3 update 7 9 Ø 25 12 19 21 56