介绍
斐波那契堆的基础理论在算法导论第三版第19章有详细介绍,这里不再赘述。
斐波那契堆 VS 二项堆
斐波那契堆与二项堆相比,在插入和堆合并上,有显著优势。
代码实现
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;
import java.util.function.Consumer;
/**
* 斐波那锲堆
*
* @author: zyp
* @since: 2022/1/18 上午10:36
*/
public class FibHeap<T> {
private TreeMap<Integer, Node> indexMap = new TreeMap<>();
private int fib1 = 1;
private int fib2 = 2;
private Node<T> min;
private int size; // 节点数
private int degree;
/**
* O(1)
*/
public void insert(int key, T value) {
Node node = indexMap.get(key);
if (node != null) {
return;
}
node = new Node(key, value);
indexMap.put(key, node);
if (min == null) {
min = node;
} else {
min.insertLeft(node);
if (node.key < min.key) {
min = node;
}
}
addSize();
}
/**
* O(1)
*/
public void clear() {
fib1 = 1;
fib2 = 2;
size = 0;
degree = 0;
min = null;
indexMap.clear();
}
/**
* O(1)
*/
public void union(FibHeap heap) {
if (heap == null || heap.min == null) {
return;
}
min.insertLeft(heap.min);
if (heap.min.key < min.key) {
min = heap.min;
}
if (size > heap.size) {
addSize(heap.size);
} else {
fib1 = heap.fib1;
fib2 = heap.fib2;
degree = heap.degree;
int tn = size;
size = heap.size;
addSize(tn);
}
}
/**
* O(1)
*/
public Integer peek() {
if (min == null) {
return null;
}
return min.key;
}
public Entry<T> peekEntry() {
if (min == null) {
return null;
}
return min.toEntry();
}
/**
* O(lgn)
*/
public Integer poll() {
Node node = popMinNode();
if (node == null) {
return null;
}
return node.key;
}
public Entry<T> pollEntry() {
Node node = popMinNode();
if (node == null) {
return null;
}
return node.toEntry();
}
public int size() {
return size;
}
/**
* O(1)
*/
public void decreaseBy(int key, int change) {
if (change == 0) {
return;
}
if (change < 0) {
throw new IllegalArgumentException("change不能为负数");
}
Node node = indexMap.get(key);
if (node == null) {
return;
}
decreaseKey(node, key - change);
}
/**
* O(lgn)
*/
public boolean delete(int key) {
Node node = indexMap.get(key);
if (node == null) {
return false;
}
delete(node);
return true;
}
public void validate() {
if (size != indexMap.size()) {
throw new RuntimeException("size 错误");
}
if (size != count()) {
throw new RuntimeException(String.format("size 错误, size=%s, count=%s", size, count()));
}
if (size > 0) {
if (peek().intValue() != indexMap.firstKey()) {
throw new RuntimeException("peek 错误");
}
}
}
public void print() {
System.out.println(String.format("============ size=%s,degree=%s ============", size, degree));
StringBuilder sb = new StringBuilder();
print(sb, min);
print(sb.toString());
}
private void print(String str) {
// System.out.println(str);
List<char[]> lines = new ArrayList<>();
int len = str.length();
int line = -1;
for (int pos = 0; pos < len; pos++) {
char c = str.charAt(pos);
if (c == '(') {
line++;
getLine(lines, len, line)[pos] = c;
} else if (c == ')') {
getLine(lines, len, line)[pos] = c;
line--;
} else {
getLine(lines, len, line)[pos] = c;
}
}
for (char[] chars : lines) {
System.out.println(new String(chars));
}
}
private char[] getLine(List<char[]> lines, int len, int line) {
if (line < lines.size()) {
return lines.get(line);
} else {
char[] data = new char[len];
Arrays.fill(data, ' ');
lines.add(data);
return data;
}
}
private void print(StringBuilder sb, Node node) {
if (node == null) {
return;
}
sb.append("(");
Node first = node;
Node cur = first;
do {
sb.append(cur.key);
if (cur.child != null) {
print(sb, cur.child);
}
sb.append(",");
cur = cur.right;
} while (cur != first);
if (sb.charAt(sb.length() - 1) == ',') {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(")");
}
/**
* 将node作为环形链表的中间节点, 返回环形链表最左边的节点
* 如果节点数是偶数, 右边会多一个节点
*/
private Node findLeft(Node node) {
if (node.left == node) {
// 1个节点
return node;
}
Node left = node.left;
Node right = node.right;
// 3个及以上
do {
if (left == right) {
return left.right;
}
if (left.left == right) {
return left;
}
left = left.left;
right = right.right;
} while (true);
}
private void changeNodeKey(Node node, int key) {
indexMap.remove(node.key);
node.key = key;
indexMap.put(key, node);
}
private void delete(Node node) {
decreaseKey(node, Integer.MIN_VALUE); // 减到最小值, min也不一定是这个节点
min = node; // 最小节点设置为node
popMinNode(); // 弹出最小节点
}
private int count() {
if (min == null) {
return 0;
}
return count(null, min);
}
private int count(Node parent, Node node) {
if (node == null) {
return 0;
}
int count = 0;
Node cur = node;
do {
if (cur.parent != parent) {
throw new RuntimeException(String.format("parent错误. parent=%s, child=%s, child.parent=%s", parent, cur, cur.parent));
}
count++;
count += count(cur, cur.child);
cur = cur.right;
} while (cur != node);
return count;
}
private void addSize() {
addSize(1);
}
private void addSize(int inc) {
size += inc;
while (size >= fib2) {
int t2 = fib2;
fib2 = fib1 + fib2;
fib1 = t2;
degree++;
}
}
private void subSize() {
if (size == 1) {
clear();
return;
}
size--;
if (size < fib1) {
int t1 = fib1;
fib1 = fib2 - fib1;
fib2 = t1;
degree--;
}
}
// 将node.key减小到key
private void decreaseKey(Node node, int decreaseTo) {
Node exist = indexMap.get(decreaseTo);
if (exist != null) {
delete(node);
return;
}
changeNodeKey(node, decreaseTo);
if (node == min) {
return;
}
Node parent = node.parent;
removeFromList(node);
min.insertLeft(node);
if (parent != null && node.key < parent.key) {
cut(node);
}
if (node.key < min.key) {
min = node;
}
}
// 级联切断
private void cascadingCut(Node node) {
Node parent = node.parent;
if (parent == null) {
return;
}
if (!node.mark) {
node.mark = true;
return;
}
cut(node);
}
// 切断
private void cut(Node node) {
Node parent = node.parent;
if (parent == null) {
return;
}
node.parent = null;
if (node.isSingle()) {
parent.degree--;
parent.child = null;
} else {
if (parent.child == node) {
parent.child = node.right;
}
removeFromList(node);
}
min.insertLeft(node);
node.mark = false;
cascadingCut(parent);
}
// 提取最小节点
private Node popMinNode() {
Node z = min;
if (z == null) {
return null;
}
indexMap.remove(z.key);
if (z.child != null) {
z.insertLeft(z.child); // 把z的所有孩子插入到根链表
min = z.right;
z.child = null;
}
removeFromList(z);
consolidate();
subSize();
return z;
}
private void removeFromList(Node node) {
Node parent = node.parent;
if (parent == null) {
// 在根链表上
if (node.isSingle()) {
// 只有一个节点
min = null;
return;
}
if (min == node) {
min = node.right;
}
} else {
parent.degree--;
// 不在根链表上
if (node.isSingle()) {
// 只有一个节点
parent.child = null;
node.parent = null;
return;
}
if (parent.child == node) {
parent.child = node.right;
}
node.parent = null;
}
Node left = node.left;
Node right = node.right;
left.right = right;
right.left = left;
node.left = node;
node.right = node;
}
// 把child设置成parent的孩子, child链表只允许1个元素
private void link(Node parent, Node child) {
if (child.left != child) {
throw new IllegalArgumentException("link.child只允许1个元素");
}
if (parent.child == null) {
parent.child = child;
parent.degree = 1;
loopList(child, i -> i.parent = parent);
} else {
parent.child.insertLeft(child);
parent.degree++;
}
child.mark = false;
}
private void consolidate() {
if (min == null) {
return;
}
Node[] degrees = new Node[degree + 1];
Node cur = min;
while (true) {
int degree = cur.degree;
Node node = degrees[degree];
if (node == null) {
degrees[degree] = cur;
cur = cur.right;
} else if (node == cur) {
break;
} else {
while (node != null) {
degrees[degree] = null;
if (node.key > cur.key) {
removeFromList(node);
link(cur, node);
} else {
removeFromList(cur);
link(node, cur);
cur = node;
}
degree++;
node = degrees[degree];
}
}
}
min = findMinNodeInList(cur);
}
// 从环形链表上找到最小节点
private Node findMinNodeInList(Node node) {
Node min = node;
Node cur = node.right;
while (cur != node) {
if (cur.key < min.key) {
min = cur;
}
cur = cur.right;
}
return min;
}
public static class Entry<T> {
private int key;
private T value;
public Entry(int key, T value) {
this.key = key;
this.value = value;
}
}
public static class Node<T> {
private Node parent;
private Node left;
private Node right;
private Node child;
private int degree;
private boolean mark;
private int key;
private T value;
public Node(int key, T value) {
this.key = key;
this.value = value;
left = this;
right = this;
}
public Entry<T> toEntry() {
return new Entry<>(key, value);
}
// 将环形链表node插入到当前节点的左边
// node是环形链表的最右边节点, node.right是环形链表的最左边节点
public void insertLeft(Node node) {
loopList(node, i -> i.parent = this.parent);
if (node.isSingle()) {
// 单个节点
left.right = node;
node.left = left;
node.right = this;
left = node;
} else {
// 多个节点
Node first = node.right;
Node last = node;
left.right = first;
first.left = left;
last.right = this;
left = last;
}
}
// 是否是单个节点
public boolean isSingle() {
return left == this;
}
public String toString() {
return String.format("%s,%s", key, degree);
}
}
// 遍历环形链表
private static void loopList(Node node, Consumer<Node> consumer) {
if (node == null) {
return;
}
Node cur = node;
do {
consumer.accept(cur);
cur = cur.right;
} while (cur != node);
}
}