D-regulární halda je n-ární úplný strom (zaplňovaný zleva doprava), ve kterém platí, že každý předek má nižší hodnotu (prioritu) než všichni jeho potomci. Tento typ uspořádání haldy se nazývá min heap, v případě opačného uspořádání hovoříme o max heap. Speciálním případem d-regulární haldy pro je
binární halda. D-regulární halda funguje díky svým vlastnostem jako prioritní fronta.
Implementace
Za předpokladu, že implemetujeme haldu pomocí pole indexovaného od 0, tak nalezneme předka prvku na indexu
. Potomky tohoto prvku nalezneme na indexech
. Je vhodné, aby
bylo mocninou čísla 2, protože pak můžeme nahradit operace násobení a dělení bitovými posuvy.
Kód
001.
/**
002.
* D-regularni halda (d-ary heap)
003.
* Implementovana jako min-heap
004.
* @author Pavel Micka
005.
*/
006.
public
class
DAryHeap<ENTITY
extends
Comparable> {
007.
008.
private
final
static
int
EXPAND_RATIO =
2
;
//kolikrat bude pole hodnot zvetseno pri expanzi
009.
private
final
static
double
COLLAPSE_RATIO =
0.25
;
//pomer pri nemz bude pole hodnot kontrahovano
010.
private
Object[] array;
011.
private
int
d;
//parametr d (arita)
012.
private
int
size;
//velikost haldy
013.
private
int
initialSize;
014.
015.
/**
016.
* Konstruktor
017.
* @param arraySize inicialni kapacita haldy
018.
*/
019.
public
DAryHeap(
int
initialSize,
int
d) {
020.
if
(d <
2
) {
021.
throw
new
IllegalArgumentException(
"D must be at least 2."
);
022.
}
023.
this
.d = d;
024.
this
.array =
new
Object[initialSize];
025.
this
.initialSize = initialSize;
026.
this
.size =
0
;
027.
}
028.
029.
/**
030.
* Vlozi prvek do haldy
031.
* Slozitost: O(log(n))
032.
* @param i prvek, ktery bude vlozen
033.
*/
034.
public
void
insert(ENTITY i) {
035.
if
(array.length == size) {
036.
expand();
037.
}
038.
size++;
039.
int
index = size -
1
;
040.
int
parentIndex = getParentIndex(index);
041.
while
(index !=
0
&& i.compareTo(array[parentIndex]) <
0
) {
//pokud je prvek mensi nez jeho otec
042.
array[index] = array[parentIndex];
//posun otce o uroven nize
043.
index = parentIndex;
//opakuj proceduru na dalsi urovni
044.
parentIndex = getParentIndex(index);
045.
}
046.
array[index] = i;
//umisti prvek na prislusnou pozici
047.
}
048.
049.
/**
050.
* Vrati a odstrani prvek na vrchu haldy
051.
* Slozitost: O(log(n))
052.
* @return prvek na vrchu haldy
053.
*/
054.
public
ENTITY returnTop() {
055.
if
(size ==
0
) {
056.
throw
new
IllegalStateException(
"Heap is empty"
);
057.
}
058.
ENTITY tmp = (ENTITY) array[
0
];
059.
array[
0
] = array[size -
1
];
060.
size--;
061.
if
(size < array.length * COLLAPSE_RATIO && array.length / EXPAND_RATIO > initialSize) {
062.
collapse();
063.
}
064.
repairTop(
0
);
065.
return
tmp;
066.
}
067.
068.
/**
069.
* Slouci danou haldu do teto haldy
070.
* Slozitost: O(n)
071.
* @param heap halda, jez bude sloucena do teto haldy
072.
*/
073.
public
void
merge(DAryHeap<ENTITY> heap) {
074.
Object[] newArray =
new
Object[array.length + heap.array.length];
075.
System.arraycopy(array,
0
, newArray,
0
, size);
076.
System.arraycopy(heap.array,
0
, newArray, size, heap.size);
077.
size = size + heap.size;
078.
array = newArray;
079.
//build heap
080.
for
(
int
i = newArray.length / d; i >=
0
; i--) {
081.
repairTop(i);
082.
}
083.
}
084.
085.
/**
086.
* Vrat index rodicovskeho uzlu stromu
087.
* @param index index prvku, pro nejz bude vyhledan rodic
088.
* @return index rodicovskeho uzlu stromu
089.
*/
090.
private
int
getParentIndex(
int
index) {
091.
return
(index -
1
) / d;
092.
}
093.
094.
/**
095.
* Umisti vrchol haldy na spravne misto v halde (tj. opravi vlasnost haldy usporadani)
096.
* @param topIndex index vrcholu haldy
097.
*/
098.
private
void
repairTop(
int
topIndex) {
099.
Comparable tmp = (Comparable) array[topIndex];
100.
int
succ = findSuccessor(topIndex * d +
1
, topIndex * d + d);
101.
while
(succ < size && tmp.compareTo(array[succ]) >
0
) {
102.
array[topIndex] = array[succ];
103.
topIndex = succ;
104.
succ = findSuccessor(succ * d +
1
, succ * d + d);
105.
}
106.
array[topIndex] = tmp;
107.
}
108.
109.
/**
110.
* Vrati index potomka s nejnizsi hodnotou
111.
* @param from index prvniho potomka
112.
* @param to index posledniho potomka
113.
* @return index index potomka s nejnizsi hodnotou
114.
*/
115.
private
int
findSuccessor(
int
from,
int
to) {
116.
int
succ = from;
117.
for
(
int
i = from +
1
; i <= to && i < size; i++) {
118.
if
(((Comparable) array[succ]).compareTo((Comparable) array[i]) >
0
) {
119.
succ = i;
120.
}
121.
}
122.
return
succ;
123.
}
124.
125.
/**
126.
* Expanduje pole hodnot
127.
*/
128.
private
void
expand() {
129.
array = Arrays.copyOf(array, array.length * EXPAND_RATIO);
130.
}
131.
132.
/**
133.
* Kontraguje pole hodnot
134.
*/
135.
private
void
collapse() {
136.
array = Arrays.copyOf(array, array.length / EXPAND_RATIO);
137.
}
138.
139.
@Override
140.
public
String toString() {
141.
StringBuilder builder =
new
StringBuilder();
142.
for
(
int
i =
0
; i < size; i++) {
143.
builder.append(array[i]).append(
" "
);
144.
}
145.
return
builder.toString();
146.
}
147.
}