Fibonacci Heap

Prerequisite: Binomial Heap

Binomial tree of degree k k k

Definition: two k − 1 k-1 k1 binomial tree merge into a k k k binomial tree
Property:

  • the root has k k k children
  • the tree has 2 k 2^k 2k elements
  • the tree has height k k k
  • the children of binomial tree are binomial trees of degree k − 1 , k − 2 , ⋯   , 1 k-1, k-2, \cdots, 1 k1,k2,,1

Binomial Heap

consists of a collection of binomial trees, at most one tree of each degree
Given a binomial heap of n items, it tell us:

  • The binary digits of n tells us which degree tree are present
  • The longest tree has degree ⌊ log ⁡ 2 n ⌋ \lfloor \log_2n \rfloor log2n and there are at most ⌊ log ⁡ 2 n ⌋ + 1 \lfloor \log_2n \rfloor + 1 log2n+1 trees

Operations

  1. Push a element into the heap
    • put the new element at the beginning
    • Recursively merge two trees of same degree, put the tree with root of smaller key at top, cost O ( 1 ) O(1) O(1) each time
    • cost O ( log ⁡ n ) O(\log n) O(logn) in total, since there are at most ⌊ log ⁡ 2 n ⌋ + 1 \lfloor \log_2n \rfloor + 1 log2n+1 trees
  2. Decrease the key of a element
    • Same as binary heap
    • cost O ( log ⁡ n ) O(\log n) O(logn), since the longest tree has degree ⌊ log ⁡ 2 n ⌋ \lfloor \log_2n \rfloor log2n
  3. Pop the min element
    • scan the tree roots to find the min element, which costs O ( log ⁡ n ) O(\log n) O(logn)
    • delete the root and promote the children, which costs O ( log ⁡ n ) O(\log n) O(logn)
    • merge trees if necessary, which costs O ( log ⁡ n ) O(\log n) O(logn)
  4. Merge two heap
    • cost O ( log ⁡ n ) O(\log n) O(logn)

Comparison

Why not a Linked-List

problem for pop min operation:
no structure maintained to tell us which is the second smallest element

popminpushdecrease keymerge
binary heap O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n ) O(\log n) O(logn) O ( n + m ) O(n+m) O(n+m)
binomial heap O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n ) O(\log n) O(logn)
O ( 1 ) O(1) O(1) amortized
O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n + log ⁡ m ) O(\log n + \log m) O(logn+logm)
linked list O ( n ) O(n) O(n) O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1)

Implementation

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ct1ufeJJ-1611309411310)(binomial heap.assets/binomial_heap_implementation.png)]

An Observastion

most of time, the push operation only needs to touch a few small elements, not the big tree of depth Ω ( log ⁡ n ) \Omega(\log n) Ω(logn), which allows it to be O ( 1 ) O(1) O(1) amortized

How to modify decreasekey to achieve a similar benefit?

popminpushdecrease keymerge
binary heap O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n ) O(\log n) O(logn) O ( n + m ) O(n+m) O(n+m)
binomial heap O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n ) O(\log n) O(logn)
O ( 1 ) O(1) O(1) amortized
O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ n + log ⁡ m ) O(\log n + \log m) O(logn+logm)
linked list O ( n ) O(n) O(n) O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1)
Fibonacci Heap O ( log ⁡ n ) O(\log n) O(logn) amortized O ( 1 ) O(1) O(1) amortized O ( 1 ) O(1) O(1) amortized

A blend of binomial heap and linked list

Fibonacci Heap

Structure & Operations

Basic Operation

consists a list of trees, each tree is a heap, of any shape

  1. push
    • put to the end of the list
    • maintain the min tag
  2. popmin
    • delete the min, O ( 1 ) O(1) O(1)
    • promote the children
    • cleanup: merge every two tree of same degree
    • maintain the min tag

summary:
lazy data structure, just dump the new element, until the next popmin operation

Reason why it’s fast:
do cleanup in batchs

Difficult Operaton: Decrease Key

  • if not heap-violating, done
  • if does, dump the node into the root list

Problem:
we might end up with a heap where the trees have Ω ( n ) \Omega(n) Ω(n) childrens, so popmin would take Ω ( n ) \Omega(n) Ω(n) time

Solution:

  • Rule1: lose one child, and you are marked a LOSER
  • Rule2: lose two child, and you are dumped into the root list
  • Rule3: losing a child becuase of Rule2 is also counted as a “lose”

Summary:
Fibonacci heap nodes have a brutal mechanism to make sure they have enough grandchildren: if your children don’t have enough children of their own, disown them.

What about Increase Key?

You may wonder why there is no increasekey. In fact, it can be implemented using the decreasekey and popmin, and takes O ( log ⁡ n ) O(\log n) O(logn) time as a result. See this answer for details.

Implementation

push

void FbHeap::push(int key, int value)
{
    Node *cur = new Node(key, value);
    dump_into_list(cur);
    if (min_node == NULL || cur->key < min_node->key) {
        min_node = cur;
    }
}

pop_min

void FbHeap::pop_min()
{
    promote_children(min_node);
    remove_node_from_tree(min_node);
    delete min_node;
    clean_up();
}

void FbHeap::promote_children(Node *root)
{
    Node* nxt; /* backup of cur->next */
    for (Node *cur = root->child; cur != NULL; cur = nxt) {
        nxt = cur->next;
        dump_into_list(cur);
    }
}

void FbHeap::clean_up()
{
/* Step 1: For any two trees of same degree, merge them */
    /* use root_arr like a hash table, whose key is degree of node */
    Node **root_arr = new Node*[MAXDEGREE+1];
    memset(root_arr, 0, (MAXDEGREE+1)*sizeof(Node*));
    /* traverse all the node in root list and add them into the hash table */
    /* at the same time, record the max degree */
    int maxD = 0;
    Node *nxt;
    for (Node *root = head->next; root != tail; root = nxt) {
        nxt = root->next; /* backup next root node */
        Node *cur = root;
        /* whenever previous node has the same degree, merge them */
        while (root_arr[cur->degree] != NULL) {
            Node *tmp = root_arr[cur->degree];
            root_arr[cur->degree] = NULL;
            cur = merge(cur, tmp);
        }
        root_arr[cur->degree] = cur;
        maxD = max(maxD, cur->degree);
    }
    /* take out nodes in hash table and create a new root list */
    Node *cur = head;
    for (int i = 0; i <= maxD; ++i) {
        if (root_arr[i] != NULL) {
            cur->next = root_arr[i];
            cur->next->prev = cur;
            cur = cur->next;
        }
    }
    cur->next = tail;
    tail->prev = cur;   
/* Step 2: update min_node */
    int min_key = INT_MAX;
    min_node = NULL;
    for (cur = head->next; cur != tail; cur = cur->next) {
        if (cur->key < min_key) {
            min_key = cur->key;
            min_node = cur;
        }
    }
    /* release memory */
    delete[] root_arr;
}

decrease_key

void FbHeap::decrease_key(Node* cur, int key)
{
    cur->key = key;
    /* if the new key isn't heap-violating, done */
    if (cur->parent == NULL || cur->key >= cur->parent->key)
        return ;
    /* otherwise, dump the cur into the root list, set the parent's LOSER tag */
    /* if it is already loser, dump it into the root list, and reset LOSER tag */
    Node* parent;
    do {
        parent = cur->parent; /* backup parent */
        remove_node_from_tree(cur);
        dump_into_list(cur);
        cur->loser = false;
        cur = parent;
    } while (cur && cur->loser);
    /* set the closest non-loser grandparent's LOSER tag */
    if (cur)
        cur->loser = true;
}

Analysis

let N N N be the number of all elements in heap

O ( log ⁡ N ) O(\log N) O(logN) Degree and popmin

call a node of k k k degree “k-node”
call a tree whose root is a k-node “k-tree”

  • A k-node has k k k childs, whose degree ≥ 0 , 0 , 1 , 2 , ⋯   , k − 2 \ge 0,0,1,2,\cdots,k-2 0,0,1,2,,k2 respectively.
    • when a k-node x is linked to k-node y, y becomes (k+1)-node
    • x lose at most one child, which means x is at least a (k-1)-node
  • let F ( k ) F(k) F(k) be the least number of nodes in a k-tree, obviously F ( 0 ) = 1 F(0)=1 F(0)=1, then F ( k ) ≥ F ( 0 ) + F ( 0 ) + F ( 1 ) + ⋯ + F ( k − 2 ) F(k) \ge F(0)+F(0)+F(1)+\cdots + F(k-2) F(k)F(0)+F(0)+F(1)++F(k2)
  • F ( k ) = F ( 0 ) + F ( 0 ) + F ( 1 ) + ⋯ + F ( k − 2 ) ⇒ F ( k + 2 ) = F ( k + 1 ) + F ( k ) ⇒ F ( k )  is Fibonacci Sequence ⇒ F ( k ) = ϕ k − 2 F(k) = F(0)+F(0)+F(1)+\cdots + F(k-2) \\ \Rightarrow F(k+2)=F(k+1)+F(k) \\ \Rightarrow F(k) \ \text{is Fibonacci Sequence} \\ \Rightarrow F(k)=\phi^{k-2} F(k)=F(0)+F(0)+F(1)++F(k2)F(k+2)=F(k+1)+F(k)F(k) is Fibonacci SequenceF(k)=ϕk2, where ϕ = ( 1 + 5 ) / 2 \phi = (1+\sqrt 5) / 2 ϕ=(1+5 )/2
  • So size of k-tree ≥ F ( k ) ≥ ϕ k − 2 ⇒ degree of tree = O ( log ⁡ N ) \text{size of k-tree} \ge F(k) \ge \phi^{k-2} \\ \Rightarrow \text{degree of tree} = O(\log N) size of k-treeF(k)ϕk2degree of tree=O(logN)
  • As a result, the amortized cost of popmin is O ( log ⁡ N ) O(\log N) O(logN)

decreasekey

everytime decrease a key, set a LOSER
so how many times you decrease a key in total, how many times could you reset a LOSER total
so every time decrease a key, reset a chain of LOSER with average length 1.
so the time complexity for decreasekey is 2 = O ( 1 ) 2=O(1) 2=O(1)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
斐波那契堆是一种数据结构,用于实现优先级队列。下面是一个简单的斐波那契堆的 Java 实现: ```java import java.util.*; public class FibonacciHeap<T> { private Node<T> maxNode; private int size; // 节点定义 private static class Node<T> { T key; int degree; boolean marked; Node<T> parent; Node<T> child; Node<T> left; Node<T> right; public Node(T key) { this.key = key; left = this; right = this; } } // 插入节点 public void insert(T key) { Node<T> node = new Node<>(key); if (maxNode != null) { node.left = maxNode; node.right = maxNode.right; maxNode.right = node; node.right.left = node; if (compare(node, maxNode) > 0) { maxNode = node; } } else { maxNode = node; } size++; } // 合并两个斐波那契堆 private void merge(FibonacciHeap<T> other) { if (other.maxNode == null) { return; } if (maxNode == null) { maxNode = other.maxNode; size = other.size; return; } Node<T> leftNode = maxNode.left; Node<T> rightNode = other.maxNode.right; maxNode.left = rightNode; rightNode.left = leftNode; leftNode.right = rightNode; rightNode.right = maxNode; if (compare(other.maxNode, maxNode) > 0) { maxNode = other.maxNode; } size += other.size; } // 提取最大节点 public T extractMax() { Node<T> max = maxNode; if (max != null) { // 将最大节点的子节点移动到根列表中 Node<T> child = max.child; while (child != null) { Node<T> nextChild = child.right; child.left = maxNode; child.right = maxNode.right; maxNode.right = child; child.right.left = child; child.parent = null; child = nextChild; } // 从根列表中移除最大节点 max.left.right = max.right; max.right.left = max.left; if (max == max.right) { maxNode = null; } else { maxNode = max.right; consolidate(); } size--; return max.key; } return null; } // 合并度数相同的树 private void consolidate() { int maxSize = (int) Math.floor(Math.log(size) / Math.log(2)) + 1; List<Node<T>> degreeTable = new ArrayList<>(maxSize); for (int i = 0; i < maxSize; i++) { degreeTable.add(null); } List<Node<T>> roots = new ArrayList<>(); Node<T> current = maxNode; Node<T> start = maxNode; do { roots.add(current); current = current.right; } while (current != start); for (Node<T> root : roots) { Node<T> node = root; int degree = node.degree; while (degreeTable.get(degree) != null) { Node<T> other = degreeTable.get(degree); if (compare(node, other) < 0) { Node<T> temp = node; node = other; other = temp; } link(other, node); degreeTable.set(degree, null); degree++; } degreeTable.set(degree, node); } maxNode = null; for (Node<T> root : roots) { if (root != null) { if (maxNode == null) { maxNode = root; } else { if (compare(root, maxNode) > 0) { maxNode = root; } } } } } // 连接两个节点 private void link(Node<T> child, Node<T> parent) { child.left.right = child.right; child.right.left = child.left; child.parent = parent; if (parent.child == null) { parent.child = child; child.right = child; child.left = child; } else { child.left = parent.child; child.right = parent.child.right; parent.child.right = child; child.right.left = child; } parent.degree++; child.marked = false; } // 比较节点 private int compare(Node<T> x, Node<T> y) { return ((Comparable<T>) x.key).compareTo(y.key); } // 获取堆的大小 public int getSize() { return size; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值