斐波那契堆(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个斐 ...
随机推荐
- day 75天 bbs 项目第一天 ,auth登陆
from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(Abst ...
- click 版本升级7.0踩过的坑
click 版本升级7.0踩过哪些坑? click 版本6.7升级至7.0以上,包名由 click 变更为 Click click 的 Options 和 Parameters 规则变更为如下: Fo ...
- BZOJ 1008--[HNOI2008]越狱(容斥&快速幂)
1008: [HNOI2008]越狱 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 12593 Solved: 5439[Submit][Status ...
- 使用pipreqs生成项目依赖
作用 导出当前项目的环境依赖 使用 # 安装 pip3 install pipreqs # 导出项目环境依赖 pipreqs ./ # 如果是Windows系统,会报编码错误 (UnicodeDeco ...
- Binary Indexed Tree-307. Range Sum Query - Mutable
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- hdoj1068 Girls and Boys(二分图的最大独立集)
题意:有n个人,要彼此认识.选择一个集合,使得集合里的每个人相互不认识.求集合中人数的最大值. 求二分图的最大独立集. 公式:最大独立集=顶点数-最大匹配 这个题目中因为集合是一个,所以求出最大匹配数 ...
- servlet中request和response
一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象 ...
- java中的IO流(输入流与输出流)概述与总结
Java中IO流,输入输出流概述与总结 总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都 ...
- day 42 mysql 数据库(2)
前情提要: 本节继续学习数据库 一:ddl 创建表 >字段名 >数据类型 >约束规则 >显示建表语句 修改表: 二:数据类型 >数值类型 >小数类型 >字 ...
- yii2之ActiveForm表单使用
因目前项目并非前后端分离模式,且用到PHP的yii2框架(所有html代码,js较多内嵌在.php文件内多少采用同步提交[喷墨中...]),遂对于前端面上需要用到的yii2小组件一些整理(因是前端若涉 ...