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

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

Github repo

简介

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

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

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

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

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

使用

我们先给出示例代码:

c 复制代码
#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头文件。

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

c 复制代码
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仓库

感谢阅读!

相关推荐
一休哥助手6 分钟前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
这可就有点麻烦了6 分钟前
强化学习笔记之【TD3算法】
linux·笔记·算法·机器学习
DY009J7 分钟前
深度探索Kali Linux的精髓与实践应用
linux·运维·服务器
落落落sss9 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
苏宸啊12 分钟前
顺序表及其代码实现
数据结构·算法
lin zaixi()15 分钟前
贪心思想之——最大子段和问题
数据结构·算法
程序员-珍18 分钟前
虚拟机ip突然看不了了
linux·网络·网络协议·tcp/ip·centos
夜雨翦春韭21 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
简单.is.good27 分钟前
【测试】接口测试与接口自动化
开发语言·python
一直学习永不止步34 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序