Двійкова купа

Матеріал з testwiki
Перейти до навігації Перейти до пошуку

Шаблон:Картка:Структура даних

Приклад повної двійкової максимальної купи. Наведено її представлення у вигляді масива.
Приклад повної двійкової мінімальної купи.

Двійкова купа[1] (Шаблон:Lang-en) — це структура даних, що є масивом, який можна розглядати як майже повне двійкове дерево. Кожен вузол цього дерева відповідає певному елементу масива. На всіх рівнях, крім, можливо останнього, дерево повністю заповнене (заповнений рівень — такий, що містить максимально можливу кількість вузлів). Останній рівень заповнюється послідовно зліва направо до тих пір, доки в масиві не закінчаться елементи.

Для масиву A у корені дерева знаходиться елемент A[1]. Далі дерево будується за наступним принципом: якщо якомусь вузлу відповідає індекс i, то індекс його батьківського вузла обчислюється за допомогою процедури Parent(i), індекс лівого дочірнього вузла — за допомогою процедури Left(i), а індекс правого дочірнього вузла — за допомогою процедури Right(i):

Parent(i)
    return i/2

Left(i)
    return 2i

Right(i)
    return 2i+1 

Розглядають два види бінарних куп: неспадні і незростаючі. В обох видах значення, що розташовані у вузлах купи, задовільняють властивості купи (Шаблон:Lang-en). Властивісь незростаючої купи (Шаблон:Lang-en) полягає в тому, що для кожного вузла крім кореневого виконується нерівність:

A[Parent(i)]A[i].

Іншими словами, значення вузла не перевищує значення батьківського вузла. Таким чином найбільший елемент знаходиться в корені дерева. Принцип побудови неспадної купи (Шаблон:Lang-en) протилежний. Властивість неспадної купи (Шаблон:Lang-en) полягає в тому, що кожен елемент крім кореневого є неменшим за свій батьківський елемент:

A[Parent(i)]A[i].

Підтримка властивостей купи

Підтримку властивості купи можна здійснювати за допомогою процедури Max_Heapify (для незростаючих бінарних куп). На вхід подається масив A й індекс i цього масиву. При виклику процедури Max_Heapify припускається, що бінарні дерева, коренями яких є елементи Left(i) і Right(i) є незростаючими купами, але сам елемент A[i] може бути меншим за його дочірні елементи і тим самим порушувати властивість незростаючої купи. Процедура Max_Heapify спускає значення елемента A[i] вниз по купі до тих пір, доки дерево в якому цей елемент буде коренем не стане незростаючою бінарною купою:

Max_Heapify(A,i)

 1 lLeft(i)
 2 rRight(i)
 3 if lheap_size[A] and A[l]>A[i]
 4     then largestl
 5     else largesti
 6 if rheap_size[A] and A[r]>A[largest]
 7     then largestr
 8 if largesti
 9     then Swap A[i]A[largest]
 10         Max_Heapify(A,largest)

Час роботи процедури в найгіршому випадку пропорційний висоті купи. Якщо купа складається з n елементів, то її висота log2(n) . Тому оцінка часу роботи одного виклику Max_Heapify є O(log n).

Для підтримки властивості неспадної бінарної купи можна скористатись процедурою Min_Heapify. Вона повністю подібна до Max_Heapify, тільки в рядках 3 і 6 алгоритму знак «>» треба замінити на «<».

Побудова купи

За допомогою процедури Max_Heapify можна перетворити масив A[1..n], де n = length[A], у незростаючу купу. Всі елементи підмасиву A[(n/2+1)..n] є листами дерева, тому кожен з них можна вважати одноелементною купою, з якої можна почати процес побудови. Процедура Build_Max_Heap проходить по всіх інших вузлах і для кожного з них виконує процедуру Max_Heapify:

Build_Max_Heap(A)

1 heap_size[A]length[A]
2 for ilength[A]/2 downto 1
3     do Max_Heapify(A,i)

По завершенню роботи процедури, масив A організується в незростаючу купу. Час роботи процедури Build_Max_Heap можна записати так:

O(nh=0log2nh2h)=O(nh=0h2h)=O(n)

Для створення неспадної купи, необхідно замінити у третьому рядку алгоритму виклик Max_Heapify на Min_Heapify.

Алгоритм впорядкування купою

Робота алгоритму сортування купою починається з виклику процедури Build_Max_H, за допомогою якої з початкового масиву A[1..n] створюється незростаюча купа. Далі послідовно з купи виймається найбільший елемент, який міняють з останнім в купі. Після кожного обміну розмір купи зменшують на одиницю. В кінці отримують повністю відсортований неспадний масив:

Heapsort(A)
1 Build_Max_Heap(A)
2 for ilength[A] downto 2
3     do Поміняти A[1]A[i]
4        heap_size[A]heap_size[A]1
5        Max_Heapify(A,1)

Час роботи процедури Heapsort рівний O(n log n), оскільки всього потрібно n-1 викликів Max_Heapify, кожен з яких працює за O(log n).

Черга з пріоритетами

Шаблон:Main

Для того, щоб реалізувати на купі операції черги з пріоритетами використовують ще одну допоміжну процедуру Un_Max_Heapify(A,i). Ця процедура підтримує властивість незростаючої купи (аналогічно Un_Min_Heapify(A,i) для неспадної купи), за умови якщо властивість купи порушується в елементі з індексом i — він може бути більшим за батьківський елемент. При цьому припускається, що в усіх інших елементах властивість виконується і батьківський елемент i-го більший кожного з нащадків i-го елемента. Процедура «піднімає» елемент угору по дереву доти, доки він не перестане порушувати властивість купи:

Un_Max_Heapify(A,i)
1 if i = 1
2    then return
3 if A[Parent(i)]<A[i]
4    then Поміняти A[Parent(i)]A[i]
5         Un_Max_Heapify(A,Parent(i))

Час роботи процедури є O(logn).

Процедура черги з пріоритетами Insert реалізується таким чином: в кінець купи дописується один елемент (при цьому розмір купи збільшується на 1), потім за допомогою Un_Max_Heapify цей елемент піднімається на необхідний рівень.

Insert(A,x)
1 heap_size[A]heap_size[A]+1
2 A[heap_size[A]]x
3 Un_Max_Heapify(A,heap_size[A])

Максимальний елемент знаходиться в першому елементі купи, тому процедура Maximum реалізується тривіально:

Maximum(A)
1 if heap_size[A] = 0
2    then Помилка "Черга пуста"
3    else return A[1]

В процедурі Extract_Max розмір купи зменшується на 1, останній елемент записується на місце першого (при цьому порушується властивість купи). Властивість купи відновлюється процедурою Max_Heapify.

Extract_Max(A)
1 if heap_size[A] = 0
2    then Помилка "Черга пуста"
3 maxA[1]
4 A[1]A[heap_size[A]]
5 heap_size[A]heap_size[A]1
6 if heap_size[A] > 0
7    then Max_Heapify(A,1)
8 return max

У процедурі Change_Key можливі три варіанти:

  1. ключ елемента збільшився
  2. ключ елемента зменшився
  3. ключ елемента не змінився

В залежності від варіанту властивість купи після зміни ключа треба відновлювати або процедурою Un_Max_Heapify або процедурою Max_Heapify:

Change_Key(A,i,k)

1 old_keyA[i]
2 A[i]k
3 if k>old_key
4     then Un_Max_Heapify(A,i)
5     elseif k < old_key
6            Max_Heapify(A,i)

Асимптотична складність операцій

Процедура Maximum виконується за 𝑂(1), процедури Insert, Extract_Max, Change_Key виконуються за 𝑂(log2𝑛).

Примітки

Шаблон:Reflist

Джерела

  • Шаблон:CLRS
  • Алгоритм сортування купою був запропонований Вільямсом (Шаблон:Citation). Він також описав, що за допомогою купи можна реалізувати чергу з пріоритетом.
  • Процедура Build_Max_Heap була розроблена Флойдом (Robert W. Floyd. Algorithm 245 (TREESORT). Communications of the ACM, 7:701, 1964).

Шаблон:Дерева (інформатика)

Шаблон:Compu-stub