Binární halda (binary heap) je úplný binární strom, ve kterém platí, že každý potomek vrcholku má nižší nebo stejnou hodnotu nežli vrcholek sám. V případě, že není poslední hladina haldy zaplněna, jsou uzly ukládány do haldy zleva.
Další důležitou vlastností je, že pokud indexujeme (od čísla 1) prvky haldy od shora dolů, zleva doprava, pak potomci každého vrcholu jsou na indexu a
, tato vlastnost je zajištěna tím, že v haldě nevynecháváme mezery. Graficky si tedy haldu můžeme představit jako pyramidu s useknutou základnou.
Vlastnost býti haldou je rekurzivní - všechny podstromy haldy jsou také haldy. Díky této vlastnosti máme zajištěno, že se halda chová jako prioritní fronta - na vrcholek haldy vždy musí vystoupit prvek s nejvyšší prioritou (čehož se využívá u heapsortu – řazení haldou).
V případě, že považujeme za vyšší prioritu nižší hodnotu klíče, hovoříme o min-heap, při opačném uspořádání o max-heap.
Operace
Repair top
Mějme haldu, u níž víme, že její vrchol nerespektuje uspořádání. Pomocná procedura repairTop přesune tento vrchol na odpovídající pozici, čímž obnoví požadované vlastnosti haldy. Samotná implementace je jednoduchá – algoritmus porovnává uzel otce s jeho syny, pokud má některý ze synů vyšší prioritu, tak jej prohodí s otcem, pokud mají oba synové vyšší prioritu, tak prohodí otce se synem s nejvyšší prioritou. Algoritmus dále iterativně pokračuje o úroveň níže, dokud buď nenarazí na nejspodnější úroveň haldy, nebo dokud není priorita otce vyšší než obou jeho synů (vlastnost býti haldou je obnovena).
Asymptotická složitost procedury repairTop je , protože halda má maximálně logaritmickou hloubku.
Heapify
Pomocná procedura heapify zkonstruuje v zadaném poli haldu. Jelikož je vlastnost býti haldou rekurzivní, tak pro konstrukci haldy musíme opravit všechny její netriviální podhaldy. Postupujeme proto po vrcholech všech netriviálních podhald (indexy ) a voláme operaci repairTop. Po ukončení výše zmíněné smyčky zadané pole reprezentuje haldu. Asymptotická složitost procedury heapify je
.
Insert
Operace vkládání (insert) funguje přesně naopak než operace repairTop. Nový prvek je přidán na konec haldy a probublává vzhůru tak dlouho, dokud má vyšší prioritu než jeho otec. Asymptotická složitost vkládání je .
Top
Operace top vrátí hodnotu prvku s nejvyšší prioritou – tj. vrcholu. Jelikož se jedná o návrat prvního prvku pole, tak má operace top konstantní asymptotickou složitost .
Return top
Operace returnTop vrátí prvek s nejvyšší prioritou a odstraní jej z haldy. Samotné smazání provede tak, že vrchol haldy nahradí jejím posledním prvkem, zkrátí haldu o 1 (dekrementuje proměnnou reprezentující prvků haldy) a zavolá operaci repairTop na vrchol haldy. Asymptotická složitost operace returnTop je totožná se složitostí volání repairTop – .
Merge
Operace merge sloučí dvě haldy do jedné – vytvoří nové pole, do nějž nakopíruje obsah obou hald a na toto nové pole zavolá operaci heapify. Asymptotická složitost tohoto postupu je , kde
je velikost první haldy a
je velikost druhé haldy.
Kód
001.
/**
002.
* Binarni halda (min-heap)
003.
* @author Pavel Micka
004.
*/
005.
public
class
BinaryHeap {
006.
007.
private
int
[] array;
008.
private
int
size;
//velikost haldy...je treba mit na pameti, ze indexujeme od 1
009.
010.
/**
011.
* Konstruktor
012.
* @param arraySize velikost haldy
013.
*/
014.
public
BinaryHeap(
int
arraySize) {
015.
this
.array =
new
int
[arraySize +
1
];
//na prvnim miste nebude nic
016.
this
.size =
0
;
017.
}
018.
019.
/**
020.
* Konstruktor
021.
* @param arraySize velikost haldy
022.
*/
023.
public
BinaryHeap(
int
[] source) {
024.
this
.array =
new
int
[source.length +
1
];
//na prvnim miste nebude nic
025.
System.arraycopy(source,
0
, array,
1
, source.length);
026.
this
.size =
0
;
027.
}
028.
029.
/**
030.
* Provede operaci slouceni hald
031.
* @param heap halda, ktera bude sloucena s touto haldou
032.
*/
033.
public
void
merge(BinaryHeap heap) {
034.
int
[] newArray =
new
int
[
this
.size + heap.size +
1
];
035.
036.
System.arraycopy(array,
1
, newArray,
1
,
this
.size);
037.
System.arraycopy(heap.array,
1
, newArray,
this
.size +
1
, heap.size);
038.
039.
size =
this
.size + heap.size;
040.
array = newArray;
041.
heapify(newArray);
042.
}
043.
044.
/**
045.
* Vrati pole vsech prvku v halde
046.
* @return pole vsech prvku v halde
047.
*/
048.
public
int
[] getAll() {
049.
return
Arrays.copyOfRange(array,
1
,
this
.size +
1
);
050.
}
051.
052.
/**
053.
* Vlozi prvek do haldy
054.
* @param i prvek k vlozeni
055.
*/
056.
public
void
insert(
int
i) {
057.
size++;
058.
int
index =
this
.size;
059.
while
(i < array[index /
2
] && index !=
0
) {
//dokud je nas prvek mensi nez jeho otec
060.
array[index] = array[index /
2
];
//pak otce posuneme o uroven niz (cimz se nam mezera na vlozeni posune o patro)
061.
index /=
2
;
//a opakujeme o uroven vys
062.
}
063.
array[index] = i;
//o patro vys je jiz prvek nizsi, proto vlozime prvek prave sem, vlastnost haldy byla obnovena
064.
}
065.
066.
public
int
top() {
067.
if
(getSize() ==
0
) {
068.
throw
new
IllegalStateException(
"halda je prazdna"
);
069.
}
070.
return
array[
1
];
071.
}
072.
073.
/**
074.
* Odstrani vrchol a vrati ho
075.
* @return vraceny vrchol
076.
*/
077.
public
int
returnTop() {
078.
if
(getSize() ==
0
) {
079.
throw
new
IllegalStateException(
"halda je prazdna"
);
080.
}
081.
int
tmp = array[
1
];
082.
array[
1
] = array[
this
.size];
083.
size--;
084.
repairTop(
this
.size,
1
);
085.
return
tmp;
086.
}
087.
088.
@Override
089.
public
String toString() {
090.
StringBuilder builder =
new
StringBuilder();
091.
for
(
int
i =
1
; i <=
this
.size; i++) {
092.
builder.append(array[i]).append(
" "
);
093.
}
094.
return
builder.toString();
095.
}
096.
097.
/**
098.
* @return the size
099.
*/
100.
public
int
getSize() {
101.
return
size;
102.
}
103.
104.
/**
105.
* Vytvor haldu ze zdrojoveho pole
106.
* @param array pole
107.
*/
108.
private
void
heapify(
int
[] array) {
109.
for
(
int
i = array.length /
2
; i >
0
; i--) {
110.
repairTop(
this
.size, i);
111.
}
112.
}
113.
114.
/**
115.
* Umisti vrchol haldy na korektni misto v halde (opravi haldu)
116.
* @param bottom posledni index pole, na ktery se jeste smi sahnout
117.
* @param topIndex index vrsku haldy
118.
*/
119.
private
void
repairTop(
int
bottom,
int
topIndex) {
120.
int
tmp = array[topIndex];
121.
int
succ = topIndex *
2
;
122.
if
(succ < bottom && array[succ] > array[succ +
1
]) {
123.
succ++;
124.
}
125.
126.
while
(succ <= bottom && tmp > array[succ]) {
127.
array[topIndex] = array[succ];
128.
topIndex = succ;
129.
succ = succ *
2
;
130.
if
(succ < bottom && array[succ] > array[succ +
1
]) {
131.
succ++;
132.
}
133.
}
134.
array[topIndex] = tmp;
135.
}
136.
}