V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
monkeyNik
V2EX  ›  C

开源 C 语言库 Melon:斐波那契堆

  •  
  •   monkeyNik · 2023-01-20 18:16:23 +08:00 · 1415 次点击
    这是一个创建于 698 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本篇介绍开源 C 语言库 Melon 的斐波那契堆的使用。关于 Melon 库,这是一个开源的 C 语言库,它具有:开箱即用、无第三方依赖、安装部署简单、中英文文档齐全等优势。

    Github repo

    简介

    关于斐波那契堆,感兴趣的朋友可以参考《算法导论》或者是各类讲解博客。

    本篇介绍的是斐波那契最小堆,但对于判断条件和初始化属性进行调整后,也可实现最大堆。

    数据结构各类操作时间复杂度:

    • 创建堆:O(1)
    • 插入:O(1)
    • 取最小值:O(1)
    • 将最小值从堆中移除:O(logN)
    • 合并堆:O(1)
    • 将堆结点 key 值减小:O(1)
    • 移除某个堆结点:O(logN)

    由此,我们可以看到斐波那契堆非常适合频繁插入和删除以及取得极值(最小值)结点操作。这样的操作第一个能想到的场景就是实现对超时定时器的管理。

    使用

    我们先给出示例代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include "mln_core.h"                                                                                                                      
    #include "mln_log.h"
    #include "mln_fheap.h"                                                                                                                     
    
    static int cmp_handler(const void *key1, const void *key2)                                                                                 
    {   
        return *(int *)key1 < *(int *)key2? 0: 1;                                                                                              
    }                                                                                                                                          
    
    static void copy_handler(void *old_key, void *new_key)                                                                                     
    {   
        *(int *)old_key = *(int *)new_key;                                                                                                     
    }                                                                                                                                          
    
    int main(int argc, char *argv[])                                                                                                           
    {   
        int i = 10, min = 0;                                                                                                                   
        mln_fheap_t *fh; 
        mln_fheap_node_t *fn; 
        struct mln_fheap_attr fattr;                                                                                                           
        struct mln_core_attr cattr;                                                                                                            
        
        cattr.argc = argc;                                                                                                                     
        cattr.argv = argv;
        cattr.global_init = NULL;
        cattr.master_process = NULL;                                                                                                           
        cattr.worker_process = NULL;
        if (mln_core_init(&cattr) < 0) {
            fprintf(stderr, "init failed\n");                                                                                                  
            return -1;                                                                                                                         
        }                                                                                                                                      
        
        fattr.pool = NULL; 
        fattr.pool_alloc = NULL;                                                                                                               
        fattr.pool_free = NULL;
        fattr.cmp = cmp_handler;
        fattr.copy = copy_handler;                                                                                                             
        fattr.key_free = NULL;                                                                                                                 
        fattr.min_val = &min;
        fattr.min_val_size = sizeof(min);                                                                                                      
        fh = mln_fheap_new(&fattr);                                                                                                            
        if (fh == NULL) {
            mln_log(error, "fheap init failed.\n");                                                                                            
            return -1;                                                                                                                         
        }                                                                                                                                      
        
        fn = mln_fheap_node_new(fh, &i);                                                                                                       
        if (fn == NULL) {
            mln_log(error, "fheap node init failed.\n");                                                                                       
            return -1;                                                                                                                         
        }
        mln_fheap_insert(fh, fn);                                                                                                              
        
        fn = mln_fheap_minimum(fh);
        mln_log(debug, "%d\n", *((int *)mln_fheap_node_key(fn)));                                                                              
        
        mln_fheap_free(fh);                                                                                                                    
        
        return 0;
    }
    

    main函数中的流程大致如下:

    1. 对 Melon 库进行初始化
    2. 设置斐波那契堆初始化属性
    3. 对斐波那契堆进行初始化
    4. 创建斐波那契堆结点
    5. 将新建的斐波那契堆结点插入堆中
    6. 取斐波那契堆中最小 key 值的堆结点
    7. 销毁斐波那契堆结构

    Melon 中,使用斐波那契堆需要引入mln_fheap.h头文件。

    这里要对堆初始化属性进行一番说明:

    struct mln_fheap_attr {
        void                     *pool;
        fheap_pool_alloc_handler  pool_alloc;
        fheap_pool_free_handler   pool_free;
        fheap_cmp                 cmp;
        fheap_copy                copy;
        fheap_key_free            key_free;
        void                     *min_val;
        mln_size_t                min_val_size;
    };
    

    其中:

    • pool是用于支持用户自定义内存池之用的,该指针将于pool_allocpool_free配合使用。
    • pool_alloc是用于支持用户自定义分配内存之用,该函数指针第一个参数为pool,第二个参数是要分配的内存大小。
    • pool_free是用于支持用户自定义释放内存之用,该函数指针第一个参数为要释放的内存起始地址。
    • cmp是用于对两个堆结点所关联的用户自定义数据进行比较大小之用的。
    • copy是用于*将堆结点 key 值减小(decrease_key)*时,将新 key 值(即用户自定义结构)拷贝到老 key 值中。
    • key_free是用于对堆结点所关联的用户自定义数据进行释放之用的。
    • min_val用户自定义结构的最小值,因为这里实现的是最小堆,因此需要给出一个相对所有插入本堆的结点而言都小的最小值范例。
    • min_val_size用户自定义最小值结构所占字节数,即min_val指向的结构的字节大小。

    这些属性字段中,除cmpcopymin_valmin_val_size外,其余若无需求,则可以置NULL

    内存池和分配释放函数主要是用于堆结点的分配和释放之用。之所以不直接给出一个 Melon 实现的内存池结构指针,是因为不希望斐波那契堆代码与内存池类型强关联,这样允许斐波那契堆可以接入使用者自己定义的内存管理功能。

    关于斐波那契堆代码的演进,与此前红黑树文章基本类似,再次就不过多阐述了。

    斐波那契堆在整个 Melon 框架中的使用场景基本都是在超时定时器的维护上面。

    结语

    在 Melon 中,斐波那契堆的使用相较于红黑树而言确实少了很多,因此其演进的速度相对来说会慢一些,演化方向也会受到类似红黑树结构演进的影响。

    同时,因为使用并不是很广泛,因此此前也有用户发起了 issue ,提出了堆结构存在实现错误,并给出了问题点。当然,问题早已被修复和验证。

    在此非常感谢各位使用者的使用和反馈,你们的反馈也是 Melon 库前进的动力。

    欢迎各位对 Melon 感兴趣的读者访问其Github 仓库

    感谢阅读!

    adian
        1
    adian  
       2023-01-20 20:35:53 +08:00
    酷!
    cortexm3
        2
    cortexm3  
       2023-01-20 22:11:07 +08:00
    cool +1
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5370 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 06:57 · PVG 14:57 · LAX 22:57 · JFK 01:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.