zoukankan      html  css  js  c++  java
  • 斐波那契堆(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代码实现(仅供参考,逻辑并不十分严谨)

     1 public class FibonNode<T extends Comparable<T>> {
     2 
     3     public T key;
     4     
     5     public FibonNode<T> child;
     6     
     7     public FibonNode<T> left;
     8     
     9     public FibonNode<T> right;
    10     
    11     public boolean mark;
    12     
    13     public FibonNode<T> parent;
    14     
    15     public int degree;
    16     
    17     public FibonNode(T key){
    18         this.degree = 0;
    19         this.key = key;
    20         this.parent = null;
    21         this.child = null;
    22         this.left = null;
    23         this.right = null;
    24         this.mark = false;
    25     }
    26 }
      1 public class FibonHeap<T extends Comparable<T>> {
      2 
      3     private int keyNum;
      4 
      5     private FibonNode<T> min;
      6 
      7     /*
      8      * 保存当前指针
      9      */
     10     private FibonNode<T> current;
     11 
     12     /*
     13      * 保存各个度对应的节点,如度为1的节点对应的节点
     14      */
     15     private Map<Integer, FibonNode<T>> degreeNodes;
     16 
     17     public FibonHeap(T key) {
     18         min = new FibonNode<T>(key);
     19         keyNum += 1;
     20         min.left = min;
     21         min.right = min;
     22     }
     23 
     24     /*
     25      * 插入值
     26      */
     27     public void insert(T key) {
     28         FibonNode<T> node = new FibonNode<T>(key);
     29         insert(node);
     30     }
     31 
     32     
     33     /*
     34      * 删除最小值
     35      */
     36     public void deleteMin() {
     37         degreeNodes = new HashMap<Integer, FibonNode<T>>();
     38         removeMinNode();
     39         consolidate();
     40 
     41     }
     42     
     43     /*
     44      * 删除节点
     45      */
     46     public void deleteNode(FibonNode<T> node){
     47         T everSmall = null;
     48         decrease(node, everSmall);
     49         deleteMin();
     50     }
     51     
     52     /*
     53      * 合并堆
     54      */
     55     public FibonHeap<T> union(FibonHeap<T> heapA, FibonHeap<T> heapB){
     56         FibonNode<T> minA = heapA.min;
     57         FibonNode<T> minB = heapB.min;
     58         minA.right = minB;
     59         minA.right.left = minB.right;
     60         minB.left = minA;
     61         minB.right.left = minA.right;
     62         FibonNode<T> min = minA;
     63         if(minB.key.compareTo(minB.key) < 0){
     64             min = minB;
     65         }
     66         heapA.min = min;
     67         heapA.keyNum += heapB.keyNum;
     68         return heapA;
     69     }
     70     
     71     private void insert(FibonNode<T> node) {
     72         //插入就是直接更新左右节点就可以了
     73         min.left.right = node;
     74         node.left = min.left;
     75         node.right = min;
     76         min.left = node;
     77         T minKey = min.key;
     78         if (node.key.compareTo(minKey) < 0) {
     79             min = node;
     80         }
     81         keyNum += 1;
     82     }
     83     
     84     /*
     85      * 把节点从堆中删除
     86      */
     87     private void removeMinNode() {
     88         FibonNode<T> left = min.left;
     89         if (left == min) {
     90             //说明只剩最后一个节点了,也就是最小节点自己
     91             if (min.child != null) {
     92                 min = null;//这里不是null,应该是孩子节点中最小节点,笔者没有写完而已
     93             }
     94         } else {
     95             deleteInList(min);
     96             addChToR(min);
     97             min = left;    //    先随意选个节点作为最小节点,在随后环节会更新的
     98         }
     99         keyNum--;
    100     }
    101 
    102 
    103     /*
    104      * 把根节点合并使其所有节点的度不相等
    105      */
    106     private void consolidate() {
    107         current = min;
    108         do {
    109             current = putDegreeNodes(current);
    110             if (current.key.compareTo(min.key) < 0) {
    111                 min = current;
    112             }
    113             current = current.right;
    114         } while (current != min && current.left != current);
    115     }
    116 
    117     /*
    118      * 
    119      */
    120     private FibonNode<T> putDegreeNodes(FibonNode<T> node) {
    121         int nodeDegree = node.degree;
    122         //从map中找节点对应的度是否存在,存在说明有相同度的节点了,需要合并
    123         FibonNode<T> nodeInMap = degreeNodes.get(nodeDegree);    
    124         if (nodeInMap == null) {
    125             degreeNodes.put(nodeDegree, node);
    126         } else {
    127             if (node.key.compareTo(nodeInMap.key) < 0) {
    128                 deleteInList(nodeInMap);
    129                 nodeInMap.left = nodeInMap;
    130                 nodeInMap.right = nodeInMap;
    131                 node = fibLink(node, nodeInMap);
    132                 nodeInMap = node;
    133             } else {
    134                 deleteInList(node);
    135                 node.left = node;
    136                 node.right = node;
    137                 nodeInMap = fibLink(nodeInMap, node);
    138 
    139                 node = nodeInMap;
    140             }
    141             degreeNodes.put(nodeDegree, null);
    142             node = putDegreeNodes(node);
    143         }
    144         return node;
    145     }
    146 
    147     private FibonNode<T> fibLink(FibonNode<T> parent, FibonNode<T> child) {
    148         if (parent.child == null) {
    149             parent.child = child;
    150             
    151         } else {
    152             parent.child = insertCyle(parent.child, child);
    153         }
    154         child.parent = parent;
    155         parent.degree += 1;
    156         return parent;
    157     }
    158 
    159     /*
    160      * 从所在链中删除
    161      */
    162     private void deleteInList(FibonNode<T> node) {
    163         FibonNode<T> left = node.left;
    164         FibonNode<T> right = node.right;
    165         left.right = right;
    166         right.left = left;
    167     }
    168 
    169     /*
    170      * 插入到链中
    171      */
    172     private FibonNode<T> insertCyle(FibonNode<T> target, FibonNode<T> node) {
    173         FibonNode<T> left = target.left;
    174         left.right = node;
    175         node.left = target;
    176         node.right = target;
    177         target.left = node;
    178         return target;
    179     }
    180 
    181     /*
    182      * 把孩子节点添加到根链表中
    183      */
    184     private void addChToR(FibonNode<T> node) {
    185         FibonNode<T> aChild = node.child;
    186         if (aChild == null) {
    187             return;
    188         }
    189         do {
    190             //孩子节点循环插入根
    191             FibonNode<T> right = aChild.right;
    192             min.right = insertCyle(min.right, aChild);
    193             aChild = right;
    194 
    195         } while (aChild != node.child);
    196     }
    197     
    198     public void decrease(FibonNode<T> target, T key){
    199         FibonNode<T> parent = target.parent;
    200         if(target.key.compareTo(key) < 0){
    201             System.out.println("只能减少key值");
    202             return;
    203         }
    204         if(parent == null){
    205             //如果修改节点是根节点,直接修改
    206             target.key = key;
    207             if(key.compareTo(min.key) < 0){
    208                 //更新最小节点
    209                 min = target;
    210             }
    211             return;
    212         }
    213         if(parent.key.compareTo(key) < 0){
    214             //如果值修改稿后不违反最小堆,直接修改即可
    215             target.key = key;
    216             return;
    217         }
    218         cutAndMeld(target);
    219         parent = cascadingCut(parent);
    220     }
    221     
    222     /*
    223      * 删除节点,并合并到根中
    224      */
    225     private void cutAndMeld(FibonNode<T> target){
    226         target.parent = null;
    227         target.mark = false;
    228         insert(target);
    229     }
    230     
    231     /*
    232      * 级联删除,使其符合斐波那契堆性质
    233      */
    234     private FibonNode<T> cascadingCut(FibonNode<T> parent){
    235         if(null == parent){
    236             return null;
    237         }
    238         parent.degree--;
    239         if(parent.mark == false){
    240             parent.mark = true;
    241         }else{
    242             cutAndMeld(parent);
    243             parent = cascadingCut(parent);
    244         }
    245         return parent;
    246     }
    247     
    248     
    249 }

    参考文献

      http://staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap21.htm

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

      fibonacci-heap

      Fibonacci_heap

      

  • 相关阅读:
    JAVA地址通过百度地图API转化为经纬度
    JS验证手机号
    JAVA获取六位随机数
    JS判断是否为“YYYYMMDD”式的日期
    JAVA 通过url下载图片保存到本地
    JAVA汉字转拼音
    JS判断是否是苹果系统(ios)和安卓系统(Android)客户端
    JAVA获取请求链接中所有参数(GET请求)
    如何高效学习 Kubernetes 知识图谱?
    当云原生遇到混合云:如何实现“求变”与“求稳”的平衡
  • 原文地址:https://www.cnblogs.com/junyuhuang/p/4463758.html
Copyright © 2011-2022 走看看