Skip to content

4.3 Operace na Haldě

Binární minimová halda – Formalizace

Atributy haldy H

  • H.root, který je aktuální kořen
  • H.n, který udržuje aktuální počet prvků
  • H.last, který udržuje ukazatel na aktuální poslední list (nejpravější list v poslední hladině), pokud existuje

Prvek haldy \(x\)

  • klíč \(k(x)\)
  • ukazatel na otce
  • ukazatele na své syny (jsou-li v haldě obsažené)

V následujícím textu předpokládáme, že výše uvedené proměnné máme a umíme je přepočítat v čase \(O(1)\). Na závěr takovou implementaci předvedeme.


Algoritmus 4.1 (HeapInsert)

Algoritmus HeapInsert

Vložení prvku do haldy HeapInsert(\(H,k\))

Vstup

  • Halda \(H\).
  • klíč \(k\) přidávaného prvku

Výstup

  • Halda \(H\) s přidaným prvkem \(p\) s klíčem \(k\)

Idea

  • Vlastnost Tvar haldy dovoluje přidat okamžitě nový prvek na konec nejspodnější hladiny
  • Pokud by již byla plná, založíme novou hladinu.
  • Pokud je Haldové uspořádání mezi novým listem l a jeho otcem o v pořádku, můžeme skončit.
  • Pokud ne, prohodíme \(k(l)\) a \(k(o)\).
  • Tím se ale může porušit vlastnost Haldového uspořádání mezi vrcholem \(o\) a otcem vrcholu \(o\).
  • Pokud pro ně Haldové uspořádání neplatí, opět jejich klíče prohodíme, jinak končíme.
  • Toto opakujeme, nejdále však až do kořene.

Algoritmus

Algoritmus HeapInsert(H,k):
HeapInsert(H, k)
    H.n += 1
    vlož na první volnou pozici v poslední hladině H
        nový vrchol p s klíčem k
    BubbleUp(H,p)

BubbleUp(H, p)
    Dokud p ̸= H.root:
        o := otec(p)
        Pokud k(o) ≤ k(p):
            return
        prohoď k(o) a k(p)
        p := o

Časová složitost

Na každé hladině strávíme \(O(1)\) operací a procházených hladin je nejvýše logaritmicky mnoho, celkem tedy čas \(O(log(n))\)


Věta 4.3 (O koreknosti HeapInsert)

O koreknosti HeapInsert

Buď \(H\) minimová halda a \(k\) klíč. Potom struktura vzniklá voláním
HeapInsert(\(H\), \(k\)) je opět minimová halda.

Důkaz Věty 4.3
  • Pokud H je prázdná, pak je výsledkem jednoprvková halda – hotovo.
  • Jinak označme \(H'\) výsledek volání HeapInsert(\(H\), \(k\)).
  • \(H'\) má dle (3) tvar haldy.
  • Zbývá ověřit, že splňuje podmínku haldového uspořádání
  • Po přidání hodnoty \(k\) do nového listu \(l\) může existovat jen jedna hrana \(s\) porušenou podmínkou. Tento problém pomocí funkce BubbleUp posouváme směrem ke kořeni.
  • Je třeba ověřit, že nemůžeme porušit podmínku haldového uspořádání mezi nějakým otcem \(o\) a jeho (případným) druhým synem na cestě z \(l\) do kořene.
    • Pozorujeme, že hodnota \(k(o)\) v \(o\) se aplikováním BubbleUp nemohla zvýšit (může klesnout, případně zůstat stejná, pokud otec vrcholu \(o\) měl stejnou hodnotu).

Algoritmus 4.2 (HeapExtractMin)

Algoritmus HeapExtractMin

  • Nalezení minima HeapFindMin(\(H\)) je triviální -> Vrátíme klíč kořene haldu v čase \(O(1)\)
  • HeapExtractMin(H) je komplikovanější: Odstranění kořene r = H.root stromu haldy by porušilo vlastnost tvar haldy.
  • Nicméně je možné triviálně odstranit poslední list \(l\), čili list nejvíc vpravo v poslední hladině.

Odstranění minima z haldy HeapExtractMin(\(H\))

Vstup

  • Halda \(H\).

Výstup

  • Halda \(H\) s odebraným prvkem \(p\) s nejmenším klíčem \(k\)

Idea

  • Prohoď \(k(r)\) a \(k(l)\)
  • Odstraň vrchol \(l\) a zmenši haldu o 1 prvek.
  • "Probublávej dolů" z kořene klíč \(k(l)\) na jeho správné místo, aby opět platilo haldové uspořádání.

Algoritmus

Algoritmus HeapExtractMin(H):
Algoritmus HeapExtractMin(H)
    r = H.root, x = k(r), l = H.last
    prohoď k(r) a k(l)
    H.n := H.n − 1
    BubbleDown(r)
    return x


BubbleDown(v)
    Dokud má v nějaké syny:
        s := takový syn v, který má nejmenší klíč
        Pokud k(v) ≤ k(s):
            return
        prohoď k(v) a k(s)
        v := s

Časová složitost

Na každé hladině strávíme \(O(1)\) operací a procházených hladin je nejvýše logaritmicky mnoho, celkem tedy čas \(O(log(n))\)


Věta 4.4 (O koreknosti HeapExtractMin)

O koreknosti HeapExtractMin

Buď \(H\) minimová halda a \(k\) klíč. Potom struktura vzniklá voláním
HeapExtractMin(\(H\)) je opět minimová halda.

Důkaz Věty 4.4
  • Tvar haldy je opět zachován triviálně.
  • Pokud je výsledkem prázdná či jednoprvková halda, máme hotovo
  • Prohození z řádku 3 mohlo porušit haldové uspořádání (mezi hladinami 0 a 1).
  • Opět prohazujeme s menším ze synů, čímž posunujeme porušení na dvojici následujících hladin.
  • Toto může pokračovat jen do poslední hladiny.

K Zamyšlení

  • Co se změní, když místo minimové haldy budeme pracovat s maximovou haldou a místo operací HeapExtractMin a HeapFindMin budeme tedy podporovat operace HeapExtractMax a HeapFindMax?
  • Rozmyslete algoritmus operace HeapChangeKey, která dostane ukazatel na prvek uložený v haldě, ve kterém se má klíč změnit na zadanou hodnotu tak, aby výsledkem byla opět halda.
  • Využitím operace HeapChangeKey lze definovat operaci, která umožňuje z haldy odstranit libovolný prvek zadaný ukazatelem. Popište její algoritmus.