前言

  斐波那契堆(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

  斐波那契堆(一)之 图文解析 和 C语言的实现

  fibonacci-heap

  Fibonacci_heap

  

斐波那契堆(Fibonacci heap)原理详解(附java代码实现)的更多相关文章

  1. LeetCode 873. 最长的斐波那契子序列的长度 题目详解

    题目详情 如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的: n >= 3 对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_ ...

  2. 斐波那契堆(一)之 图文解析 和 C语言的实现

    概要 本章介绍斐波那契堆.和以往一样,本文会先对斐波那契堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了 ...

  3. 斐波那契堆(二)之 C++的实现

    概要 上一章介绍了斐波那契堆的基本概念,并通过C语言实现了斐波那契堆.本章是斐波那契堆的C++实现. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆的C++实现(完整源码)4.  ...

  4. 斐波那契堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了斐波那契堆,本章给出斐波那契堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆 ...

  5. fibonacci-Heap(斐波那契堆)原理及C++代码实现

    斐波那契堆是一种高级的堆结构,建议与二项堆一起食用效果更佳. 斐波那契堆是一个摊还性质的数据结构,很多堆操作在斐波那契堆上的摊还时间都很低,达到了θ(1)的程度,取最小值和删除操作的时间复杂度是O(l ...

  6. 基于visual Studio2013解决算法导论之045斐波那契堆

     题目 斐波那契堆 解决代码及点评 // 斐波那契堆.cpp : 定义控制台应用程序的入口点. // #include<iostream> #include<cstdio> ...

  7. 笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆

    二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者 ...

  8. 第2章 数字之魅——斐波那契(Fibonacci)数列

    斐波那契(Fibonacci)数列 问题描述 递归算法: package chapter2shuzizhimei.fibonacci; /** * Fibonacci数列递归求解 * @author ...

  9. python实现斐波那契数列(Fibonacci sequence)

    使用Python实现斐波那契数列(Fibonacci sequence) 斐波那契数列形如 1,1,2,3,5,8,13,等等.也就是说,下一个值是序列中前两个值之和.写一个函数,给定N,返回第N个斐 ...

随机推荐

  1. docker 运行容器,安装Nginx

    ########################################## #运行容器 #安装Nginx #搜索.下载镜像 docker search nginx docker pull n ...

  2. C++命令行画心形<转载>

    #include <stdio.h> int main() { for (float y = 1.5f; y > -1.5f; y -= 0.1f) { for (float x = ...

  3. Python中的运算符与表达式

    你所编写的大多数语句(逻辑行)都包含了表达式(Expressions).一个表达式的简单例子便是 2+3.表达式可以拆分成运算符(Operators)与操作数(Operands).运算符(Operat ...

  4. my SQL Workbench创建数据库

    1.首先连接数据库 2. 点击图中的红圈里的按钮,新建一个Schema,即数据库(个人理解..),修改一下Name 的值,如  mydatabase ,点击apply,再点apply,然后点finis ...

  5. 汽车检测SIFT+BOW+SVM

    项目来源于 <opencv 3计算机视觉 python语言实现> 整个执行过程如下: 1)获取一个训练数据集. 2)创建BOW训练器并获得视觉词汇. 3)采用词汇训练SVM. 4)尝试对测 ...

  6. POJ 2376

    #include<iostream>//by chengdacaizi. #include<stdio.h> #define MAXN 25005 using namespac ...

  7. (转)Python标准库02 时间与日期 (time, datetime包)

    原文:http://www.cnblogs.com/vamei/archive/2012/09/03/2669426.html https://www.liaoxuefeng.com/wiki/001 ...

  8. (转) Linux(Centos7)yum安装最新mysql

    原文:http://blog.csdn.net/gebitan505/article/details/54613549 环境 CentOS 7.1 (64-bit system) MySQL 5.6. ...

  9. 第5章—构建Spring Web应用程序—SpringMVC详解

    SpringMVC详解 5.1.跟踪Springmvc的请求 SpringMVC的核心流程如下: 具体步骤: 第一步:发起请求到前端控制器(DispatcherServlet) 第二步:前端控制器请求 ...

  10. docker 查看拉取镜像源地址

    命令:  cat /etc/docker/de