Binomiální halda (Binomial heap) je halda složená z lesa binomiálních stromů. Každý prvek každého stromu má vyšší prioritu, než kterýkoliv z jeho potomků – halda se proto chová jako prioritní fronta. Binomiální halda má oproti binární a d-regulární haldě výhodu v rychlejších operacích sloučení hald () a vložení prvku (amortizovaně ).
Binomiální strom
Binonomiální strom řádu 0 obsahuje právě jeden uzel. Binomiální strom řádu n vznikne sloučením dvou stromů řádu takovým způsobem, že porovnáme prioritu kořenů obou stromů a následně ten s nižší prioritou zavěsíme pod kořen s vyšší prioritou. Binomiální halda obsahuje maximálně jeden strom každého řádu.
- Binomiální strom řádu n obsahuje uzlů.
- Binomiální strom řádu n má hloubku n.
- Kořen binomiální stromu má n potomků.
- Kořen binomiálního stromu obsahuje jako své potomky všechny stromy nižších řádů.
Základní operace binomiální haldy
Operace jsou popsány pro min-heap – nižší prvek má vyšší prioritu. Max-heap (tj. vyšší prvek má vyšši prioritu) funguje analogicky.
Insert
Vloží prvek do haldy. Vytvoří pro prvek binomiální strom řádu 0 a provede sloučení tohoto stromu s haldou. Pokud vkládáme prvek s nižší prioritou, než je priorita současného nejnižšího prvku haldy, tak tento prvek označíme za minimální. Asymptotická složitost operace je , amortizovaná složitost .
Return top
Vrátí a smaže z haldy prvek s nejvyšší prioritou. Odebere minimální prvek haldy, všechny podstromy přidá zpět do haldy prostřednictvím operace merge. Nalezne nový nejnižší prvek průchodem přes všechny kořeny binomiálních stromů. Asymptotická složitost operace je .
Merge
Provede sloučení haldy B do haldy A. Tato operace odpovídá sčítání dvou binárních čísel. Algoritmus prochází obě haldy od nejnižšího řádu, pokud halda B obsahuje strom řádu n a halda A nikoliv, pak jej pouze překopíruje. Pokud obě haldy obsahují stromy stejného řádu, tak je sloučí do přenosového stromu (obdoba přenosového bitu u sčítání), který zohlední pří následném spojování stromů řádu . Nakonec porovná minimální prvky obou slučovaných hald a za minimální prvek výsledné haldy označí nižší z nich. Asymptotická složitost operace je .
Kód
/** * Binomialni halda * Radi prvky dle priority (mensi == vyssi priorita) * @author Pavel Micka */ public class BinomialHeap<ENTITY extends Comparable> { private Node<ENTITY>[] nodes; private Node<ENTITY> min; /** * Konstruktor * @param capacity kapacita haldy */ public BinomialHeap(int capacity) { min = null; nodes = new Node[((int) (Math.log(capacity) / Math.log(2))) + 2]; } /** * Vlozi prvek do haldy, pokud je mensi nez aktualni minimalni, tak * jej ulozi jako nejmensi prvek * @param e */ public void insert(ENTITY e) { Node<ENTITY> n = new Node<ENTITY>(e); if (nodes[0] != null) merge(n, nodes[0]); else nodes[0] = n; if (min == null) min = n; else if (min.value.compareTo(n.value) == 1) min = n; } /** * Smaze a vrati entitu s nejvyssi prioritou * @return entita s nejvyssi prioritou, @null, pokud je halda prazdna */ public ENTITY returnTop() { if(min == null) return null; ENTITY tmp = min.value; nodes[min.order] = null; //strom vyjmeme for (Node n : min.children) { //a z potomku udelame nove stromu n.parent = null; if(nodes[n.order] != null) merge(n, nodes[n.order]); else nodes[n.order] = n; } min.children.clear(); ENTITY minVal = null; Node minNode = null; for(int i = 0; i < nodes.length; i++){ if(nodes[i] != null){ if(minVal == null){ minVal = nodes[i].value; minNode = nodes[i]; } else if(minVal.compareTo(nodes[i].value) == 1){ minVal = nodes[i].value; minNode = nodes[i]; } } } min = minNode; return tmp; } /** * Slouci haldy, pokud mergovana halda obsahuje mensi prvek, nez halda, na * ktere je volana operace, pak je tento prvek minimalni i ve sloucene halde * @param heap */ public void mergeHeaps(BinomialHeap<ENTITY> heap) { Node<ENTITY> carry = null; for (int i = 0; i < heap.nodes.length; i++) { if(nodes[i] == null){ if(heap.nodes[i] == null){ nodes[i] = carry; carry = null; }else{ if(carry == null){ nodes[i] = heap.nodes[i]; }else{ carry = mergeTrees(heap.nodes[i], carry); } } }else{ if(heap.nodes[i] == null){ if(carry != null){ carry = mergeTrees(nodes[i], carry); nodes[i] = null; } }else{ if(carry == null){ carry = mergeTrees(nodes[i], heap.nodes[i]); nodes[i] = null; }else{ carry = mergeTrees(carry, heap.nodes[i]); } } } } if(carry != null) throw new RuntimeException("Preteceni haldy (halda je preplnena)"); if (this.min == null) this.min = heap.min; else if(this.min != null && heap.min != null && heap.min.value.compareTo(min.value) == -1) min = heap.min; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for(int i = 0; i < nodes.length; i++){ if(nodes[i] == null) builder.append(i + ": null\\n"); else builder.append(i + ":\\n" + nodes[i].toString() + "\\n"); } return builder.toString(); } /** * Provede slouceni binomialnich stromu (vcetne preteceni do vyssich radu) * @param a * @param b */ private void merge(Node a, Node b) { if (a.order != b.order) throw new IllegalArgumentException("Stromy nejsou stejneho radu"); int tmpOrder = a.order; nodes[tmpOrder] = null; Node newRoot = null; newRoot = mergeTrees(a, b); if (nodes[tmpOrder + 1] == null) nodes[tmpOrder + 1] = newRoot; else merge(newRoot, nodes[tmpOrder + 1]); } /** * Slouci binomialni stromy v jeden (zavesi strom s nizsi prioritou pod strom * s vyssi prioritou) * @param a strom a * @param b strom b * @return */ private Node mergeTrees(Node a, Node b) { Node newRoot = null; if (a.value.compareTo(b.value) < 0) { b.parent = a; a.children.add(b); a.order++; newRoot = a; } else { a.parent = b; b.children.add(a); b.order++; newRoot = b; } return newRoot; } /** * Reprezentuje uzel binomialni haldy * @param <ENTITY> */ private class Node<ENTITY extends Comparable> { Node<ENTITY> parent; ENTITY value; List<Node> children; int order; //rad binomialni haldy public Node(ENTITY value) { this.value = value; children = new ArrayList<Node>(); order = 0; } @Override public String toString() { String s = "Node value: " + value + ", order: " + order + "\\n"; for(Node n : children){ s += n.toString(); } return s; } } }