斐波那契堆(Fibonacci heap)原理详解(附java代码实现)
前言
斐波那契堆(Fibonacci heap)是计算机科学中最小堆有序树的集合。它和二项式堆有类似的性质,但比二项式堆有更好的均摊时间。堆的名字来源于斐波那契数,它常用于分析运行时间。

堆结构介绍
基本术语介绍:
关键字:堆节点储存的用于比较的信息
度数:堆节点拥有的孩子数(注意,不包括孩子的孩子)
左兄弟:节点左边的兄弟节点
右兄弟:节点右边的兄弟节点
mark:是否有孩子节点被删除
斐波那契堆是一系列无序树的集合,每棵树是一个最小堆,满足最小堆的性质。(注意,树是无序的,所以不要纠结树该怎么排序)。堆保存了堆中所有节点的数目,保存了最小关键字的节点(这是整个堆的唯一入口,根据这个最小节点可以获取整个堆的任何节点)。
堆的节点是堆的最小单位,它是双向链表的节点,意味着它保存了上下节点的信息,如下图,(也能看出树的根节点排列是无序的)。

它主要有如下性质:
1、关键字
2、度数
3、左兄弟
4、右兄弟
5、父节点
6、孩子节点(任一个孩子节点,随意)
堆基本操作
一、插入操作
1、创建一个节点,如21

2、把新建的节点插入到根链表中,如果是最小值,则更新它为堆的最小节点。插入位置没有规定,一般习惯插入到min的左边。把堆的“所有节点数”值加1

3、插入操作完成了(插入并不会对堆进行修改,修改是在其他操作中进行的,所以比较简单)
二、删除最小节点
1、删除最小节点,并把它的所有孩子合并到堆的根链表中,并更新min



2、合并根节点的树,使任何树的度(rank)不相等
观察到7有1个孩子节点,即度为1,先保存起来,由于是初始的,肯定没有和7度相同的

接着下一个根节点24,度为2,继续。

23, 度为1,继续

17, 度为1。 由于已经有度为1的根节点了,所以需要合并这两个节点

根据最小堆得性质,把23合并到17上,作为17的孩子节点

此时17的度为2,仍然重复,继续合并,直到没有度一样的根节点






最终结果如下图

三、减小key值
如果没有违背最小堆的性质,直接减小key的值
否则,把以key为根节点的树合并到堆的根链表中
如果有一个节点有两个孩子移除了,把这个节点也合并到根链表中,并且unmark它

现在举一个例子来说明各种可能情况
1、不违反最小堆性质
把46减小为29,不违反最小堆性质,不改变堆结构

2、违反最小堆性质,合并到根链表中,并且unmark 它
把29减小为15,违反了堆性质

把15合并到根链表中

如果父节点没有mark(没有失去孩子), 设置它为mark

如果父节点已经是mark,则把父节点合并到根链表中,并设置为unmark。
把节点35减小到5

由于违反了,把5合并到根

由于26已经mark,把26这个子树合并到根

同理24合并到根

由于7已经是根节点了,停止,全部结束

四、删除节点
删除节点比较简单,主要分为两步
1、把节点值decrease比堆最小值还小
2、删除最小值
java代码实现(仅供参考,逻辑并不十分严谨)
public class FibonNode<T extends Comparable<T>> {
public T key;
public FibonNode<T> child;
public FibonNode<T> left;
public FibonNode<T> right;
public boolean mark;
public FibonNode<T> parent;
public int degree;
public FibonNode(T key){
this.degree = 0;
this.key = key;
this.parent = null;
this.child = null;
this.left = null;
this.right = null;
this.mark = false;
}
}
public class FibonHeap<T extends Comparable<T>> {
private int keyNum;
private FibonNode<T> min;
/*
* 保存当前指针
*/
private FibonNode<T> current;
/*
* 保存各个度对应的节点,如度为1的节点对应的节点
*/
private Map<Integer, FibonNode<T>> degreeNodes;
public FibonHeap(T key) {
min = new FibonNode<T>(key);
keyNum += 1;
min.left = min;
min.right = min;
}
/*
* 插入值
*/
public void insert(T key) {
FibonNode<T> node = new FibonNode<T>(key);
insert(node);
}
/*
* 删除最小值
*/
public void deleteMin() {
degreeNodes = new HashMap<Integer, FibonNode<T>>();
removeMinNode();
consolidate();
}
/*
* 删除节点
*/
public void deleteNode(FibonNode<T> node){
T everSmall = null;
decrease(node, everSmall);
deleteMin();
}
/*
* 合并堆
*/
public FibonHeap<T> union(FibonHeap<T> heapA, FibonHeap<T> heapB){
FibonNode<T> minA = heapA.min;
FibonNode<T> minB = heapB.min;
minA.right = minB;
minA.right.left = minB.right;
minB.left = minA;
minB.right.left = minA.right;
FibonNode<T> min = minA;
if(minB.key.compareTo(minB.key) < 0){
min = minB;
}
heapA.min = min;
heapA.keyNum += heapB.keyNum;
return heapA;
}
private void insert(FibonNode<T> node) {
//插入就是直接更新左右节点就可以了
min.left.right = node;
node.left = min.left;
node.right = min;
min.left = node;
T minKey = min.key;
if (node.key.compareTo(minKey) < 0) {
min = node;
}
keyNum += 1;
}
/*
* 把节点从堆中删除
*/
private void removeMinNode() {
FibonNode<T> left = min.left;
if (left == min) {
//说明只剩最后一个节点了,也就是最小节点自己
if (min.child != null) {
min = null;//这里不是null,应该是孩子节点中最小节点,笔者没有写完而已
}
} else {
deleteInList(min);
addChToR(min);
min = left; // 先随意选个节点作为最小节点,在随后环节会更新的
}
keyNum--;
}
/*
* 把根节点合并使其所有节点的度不相等
*/
private void consolidate() {
current = min;
do {
current = putDegreeNodes(current);
if (current.key.compareTo(min.key) < 0) {
min = current;
}
current = current.right;
} while (current != min && current.left != current);
}
/*
*
*/
private FibonNode<T> putDegreeNodes(FibonNode<T> node) {
int nodeDegree = node.degree;
//从map中找节点对应的度是否存在,存在说明有相同度的节点了,需要合并
FibonNode<T> nodeInMap = degreeNodes.get(nodeDegree);
if (nodeInMap == null) {
degreeNodes.put(nodeDegree, node);
} else {
if (node.key.compareTo(nodeInMap.key) < 0) {
deleteInList(nodeInMap);
nodeInMap.left = nodeInMap;
nodeInMap.right = nodeInMap;
node = fibLink(node, nodeInMap);
nodeInMap = node;
} else {
deleteInList(node);
node.left = node;
node.right = node;
nodeInMap = fibLink(nodeInMap, node);
node = nodeInMap;
}
degreeNodes.put(nodeDegree, null);
node = putDegreeNodes(node);
}
return node;
}
private FibonNode<T> fibLink(FibonNode<T> parent, FibonNode<T> child) {
if (parent.child == null) {
parent.child = child;
} else {
parent.child = insertCyle(parent.child, child);
}
child.parent = parent;
parent.degree += 1;
return parent;
}
/*
* 从所在链中删除
*/
private void deleteInList(FibonNode<T> node) {
FibonNode<T> left = node.left;
FibonNode<T> right = node.right;
left.right = right;
right.left = left;
}
/*
* 插入到链中
*/
private FibonNode<T> insertCyle(FibonNode<T> target, FibonNode<T> node) {
FibonNode<T> left = target.left;
left.right = node;
node.left = target;
node.right = target;
target.left = node;
return target;
}
/*
* 把孩子节点添加到根链表中
*/
private void addChToR(FibonNode<T> node) {
FibonNode<T> aChild = node.child;
if (aChild == null) {
return;
}
do {
//孩子节点循环插入根
FibonNode<T> right = aChild.right;
min.right = insertCyle(min.right, aChild);
aChild = right;
} while (aChild != node.child);
}
public void decrease(FibonNode<T> target, T key){
FibonNode<T> parent = target.parent;
if(target.key.compareTo(key) < 0){
System.out.println("只能减少key值");
return;
}
if(parent == null){
//如果修改节点是根节点,直接修改
target.key = key;
if(key.compareTo(min.key) < 0){
//更新最小节点
min = target;
}
return;
}
if(parent.key.compareTo(key) < 0){
//如果值修改稿后不违反最小堆,直接修改即可
target.key = key;
return;
}
cutAndMeld(target);
parent = cascadingCut(parent);
}
/*
* 删除节点,并合并到根中
*/
private void cutAndMeld(FibonNode<T> target){
target.parent = null;
target.mark = false;
insert(target);
}
/*
* 级联删除,使其符合斐波那契堆性质
*/
private FibonNode<T> cascadingCut(FibonNode<T> parent){
if(null == parent){
return null;
}
parent.degree--;
if(parent.mark == false){
parent.mark = true;
}else{
cutAndMeld(parent);
parent = cascadingCut(parent);
}
return parent;
}
}
参考文献
http://staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap21.htm
斐波那契堆(Fibonacci heap)原理详解(附java代码实现)的更多相关文章
- LeetCode 873. 最长的斐波那契子序列的长度 题目详解
题目详情 如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的: n >= 3 对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_ ...
- 斐波那契堆(一)之 图文解析 和 C语言的实现
概要 本章介绍斐波那契堆.和以往一样,本文会先对斐波那契堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了 ...
- 斐波那契堆(二)之 C++的实现
概要 上一章介绍了斐波那契堆的基本概念,并通过C语言实现了斐波那契堆.本章是斐波那契堆的C++实现. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆的C++实现(完整源码)4. ...
- 斐波那契堆(三)之 Java的实现
概要 前面分别通过C和C++实现了斐波那契堆,本章给出斐波那契堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆 ...
- fibonacci-Heap(斐波那契堆)原理及C++代码实现
斐波那契堆是一种高级的堆结构,建议与二项堆一起食用效果更佳. 斐波那契堆是一个摊还性质的数据结构,很多堆操作在斐波那契堆上的摊还时间都很低,达到了θ(1)的程度,取最小值和删除操作的时间复杂度是O(l ...
- 基于visual Studio2013解决算法导论之045斐波那契堆
题目 斐波那契堆 解决代码及点评 // 斐波那契堆.cpp : 定义控制台应用程序的入口点. // #include<iostream> #include<cstdio> ...
- 笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆
二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者 ...
- 第2章 数字之魅——斐波那契(Fibonacci)数列
斐波那契(Fibonacci)数列 问题描述 递归算法: package chapter2shuzizhimei.fibonacci; /** * Fibonacci数列递归求解 * @author ...
- python实现斐波那契数列(Fibonacci sequence)
使用Python实现斐波那契数列(Fibonacci sequence) 斐波那契数列形如 1,1,2,3,5,8,13,等等.也就是说,下一个值是序列中前两个值之和.写一个函数,给定N,返回第N个斐 ...
随机推荐
- 【2019】OCP 12c 062题库更新大量新题-7
7.daily_ords_lst is created in locally managed tablespace ORDERS_TBS which uses automatic segment sp ...
- BZOJ 1008--[HNOI2008]越狱(容斥&快速幂)
1008: [HNOI2008]越狱 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 12593 Solved: 5439[Submit][Status ...
- jzoj4424
20%:暴力枚舉每一條邊有沒有被選到,然後使用并查集判斷聯通性 這樣子有20分,但是我考試寫掛了所以1分也沒有 100%:這道題2000的數據範圍,使用指數級搜索會tle,需要更加好的方法 這道題中, ...
- Flask的WTforms
一.简单介绍 WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 类似于Django中的modelform 安装: pip3 install wtforms 二.简 ...
- Kafka数据可靠性与一致性解析
Partition Recovery机制 每个Partition会在磁盘记录一个RecoveryPoint, 记录已经flush到磁盘的最大offset.broker fail 重启时,会进行load ...
- nginx 开启GZIP、域名指向index.html
nginx 虽然默认开启了gzip压缩,但是有关压缩文件.压缩效率没有开启,在建设我的(个人博客)[www.fayinme.cn]中,直观的感受到gzip带来的访问速度提升的快感. 如何开启GZIP ...
- Java线程代码实现
线程的Java实现 参考博客:(http://www.importnew.com/20672.html) 1.继承Thread 声明Thread的子类; 这种方法是创建类继承Thread,然后重写Th ...
- easyUI的分页,只显示第X 共Y页。改为显示 第X 页 共Y页
如下图,easyUI的分页,只显示第X 共Y页. 需求需要显示 第X 页 共Y页. 解决办法:在easyui-lang-zh_CN.js更改以下代码,即可.也就是在 “共{pages}页”前面加个 “ ...
- 解决Mysql Workbench的Error Code: 1175错误 无法删除数据
使用workbench,如果你要批量更新或删除数据,一般会报“ Error Code: 1175 You are using safe update mode and you tried to upd ...
- Ubuntu链接库查找
apt-get install apt-file apt-get update apt-file search