D-regulární halda

D-regulární halda
D-regulární halda

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 d = 2 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 k na indexu (k-1)/d. Potomky tohoto prvku nalezneme na indexech d \cdot k + 1, \cdots ,\, d \cdot k + d. Je vhodné, aby d bylo mocninou čísla 2, protože pak můžeme nahradit operace násobení a dělení bitovými posuvy.

Kód

/**
 * D-regularni halda (d-ary heap)
 * Implementovana jako min-heap
 * @author Pavel Micka
 */
public class DAryHeap<ENTITY extends Comparable> {

    private final static int EXPAND_RATIO = 2; //kolikrat bude pole hodnot zvetseno pri expanzi
    private final static double COLLAPSE_RATIO = 0.25; //pomer pri nemz bude pole hodnot kontrahovano
    private Object[] array;
    private int d; //parametr d (arita)
    private int size; //velikost haldy
    private int initialSize;

    /**
     * Konstruktor
     * @param arraySize inicialni kapacita haldy
     */
    public DAryHeap(int initialSize, int d) {
        if (d < 2) {
            throw new IllegalArgumentException("D must be at least 2.");
        }
        this.d = d;
        this.array = new Object[initialSize];
        this.initialSize = initialSize;
        this.size = 0;
    }

    /**
     * Vlozi prvek do haldy
     * Slozitost: O(log(n))
     * @param i prvek, ktery bude vlozen
     */
    public void insert(ENTITY i) {
        if (array.length == size) {
            expand();
        }
        size++;
        int index = size - 1;
        int parentIndex = getParentIndex(index);
        while (index != 0 && i.compareTo(array[parentIndex]) < 0) { //pokud je prvek mensi nez jeho otec
            array[index] = array[parentIndex]; //posun otce o uroven nize
            index = parentIndex; //opakuj proceduru na dalsi urovni
            parentIndex = getParentIndex(index);
        }
        array[index] = i; //umisti prvek na prislusnou pozici
    }

    /**
     * Vrati a odstrani prvek na vrchu haldy
     * Slozitost: O(log(n))
     * @return prvek na vrchu haldy
     */
    public ENTITY returnTop() {
        if (size == 0) {
            throw new IllegalStateException("Heap is empty");
        }
        ENTITY tmp = (ENTITY) array[0];
        array[0] = array[size - 1];
        size--;
        if (size < array.length * COLLAPSE_RATIO && array.length / EXPAND_RATIO > initialSize) {
            collapse();
        }
        repairTop(0);
        return tmp;
    }

    /**
     * Slouci danou haldu do teto haldy
     * Slozitost: O(n)
     * @param heap halda, jez bude sloucena do teto haldy
     */
    public void merge(DAryHeap<ENTITY> heap) {
        Object[] newArray = new Object[array.length + heap.array.length];
        System.arraycopy(array, 0, newArray, 0, size);
        System.arraycopy(heap.array, 0, newArray, size, heap.size);
        size = size + heap.size;
        array = newArray;
        //build heap
        for (int i = newArray.length / d; i >= 0; i--) {
            repairTop(i);
        }
    }

    /**
     * Vrat index rodicovskeho uzlu stromu
     * @param index index prvku, pro nejz bude vyhledan rodic
     * @return index rodicovskeho uzlu stromu
     */
    private int getParentIndex(int index) {
        return (index - 1) / d;
    }

    /**
     * Umisti vrchol haldy na spravne misto v halde (tj. opravi vlasnost haldy usporadani) 
     * @param bottom index posledniho prvku haldy
     * @param topIndex index vrcholu haldy
     */
    private void repairTop(int topIndex) {
        Comparable tmp = (Comparable) array[topIndex];
        int succ = findSuccessor(topIndex * d + 1, topIndex * d + d);
        while (succ < size && tmp.compareTo(array[succ]) > 0) {
            array[topIndex] = array[succ];
            topIndex = succ;
            succ = findSuccessor(succ * d + 1, succ * d + d);
        }
        array[topIndex] = tmp;
    }

    /**
     * Vrati index potomka s nejnizsi hodnotou
     * @param from index prvniho potomka
     * @param to index posledniho potomka
     * @return index index potomka s nejnizsi hodnotou
     */
    private int findSuccessor(int from, int to) {
        int succ = from;
        for (int i = from + 1; i <= to && i < size; i++) {
            if (((Comparable) array[succ]).compareTo((Comparable) array[i]) > 0) {
                succ = i;
            }
        }
        return succ;
    }

    /**
     * Expanduje pole hodnot
     */
    private void expand() {
        array = Arrays.copyOf(array, array.length * EXPAND_RATIO);
    }

    /**
     * Kontraguje pole hodnot
     */
    private void collapse() {
        array = Arrays.copyOf(array, array.length / EXPAND_RATIO);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            builder.append(array[i]).append(" ");
        }
        return builder.toString();
    }
}
{ zpětná vazba }
Delicious Delicious
Sdílet
Hodnocení (0): 0

Přečtěte si také

Diskuse





Pavel Mička21.1.2012
> adrian

Díky, opraveno.
adrian20.1.2012
Myslím, že vo vete "Je vhodné, aby k bylo mocninou čísla 2, protože pak můžeme nahradit operace násobení a dělení bitovými posuvy." má byť namiesto "k" "d".