堆的建立、插入、出堆、堆化、topk问题、堆排序(C语言实现)

2024-09-03 01:12

本文主要是介绍堆的建立、插入、出堆、堆化、topk问题、堆排序(C语言实现),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

堆的建立、插入、出堆、堆化、topk问题、堆排序

  • 使用数组来存储堆
    • 堆顶为序号0,堆底为序号 size - 1
  • 假设树为完全二叉树,当前节点和双亲节点的关系可以通过公式表达
// 小顶堆: 对 heaptifyUp 和 heaptifyDown 函数的逻辑进行一些调整。
void initHeap(float **arr, int *size) { *arr = (float *)malloc(sizeof(float) * MAX_SIZE); *size = 0; }
void peekTop(float *arr, int size, float *x) { if (size <= 0) return; *x = arr[0]; }
void getParent(int i, int *p_idx) { *p_idx = (i - 1) / 2; }
void getLeftChild(int i, int *lc_idx) { *lc_idx = 2 * i + 1; }
void getRightChild(int i, int *rc_idx) { *rc_idx = 2 * i + 2; }
void heaptifyUp(float *arr, int idx) { // 比较当前节点与其父节点的大小。如果当前节点的值小于父节点的值,则交换它们,并继续向上调整。int cur = idx;while (cur > 0) {  // 只要 cur 不是根节点int par; getParent(cur, &par); if (arr[cur] >= arr[par]) break; // 如果当前节点大于等于父节点,退出循环float tmp = arr[cur]; arr[cur] = arr[par]; arr[par] = tmp; cur = par;  // 交换当前节点和父节点, 更新 cur 为父节点索引}
}
void heaptifyDown(float *arr, int size, int idx) { // 比较当前节点与其左右子节点的大小。选择最小的子节点,如果当前节点大于这个子节点,则交换它们,并继续向下调整。int cur = idx;while (true) {int lc, rc, minidx = cur; getLeftChild(cur, &lc); getRightChild(cur, &rc);if (lc < size && arr[lc] < arr[minidx]) { minidx = lc; } // 找到当前节点和左子节点中的最小值if (rc < size && arr[rc] < arr[minidx]) { minidx = rc; } // 找到当前节点和右子节点中的最小值if (minidx == cur) break; // 如果当前节点是最小值,退出循环float tmp = arr[cur]; arr[cur] = arr[minidx]; arr[minidx] = tmp; cur = minidx;  // 交换当前节点和最小值节点, 更新 cur 为新的索引}
}
void pushHeap(float *arr, int *size, float x) {if ((*size) >= MAX_SIZE) return; // 如果堆已满,直接返回arr[*size] = x; (*size)++; // 将新元素放在堆的最后一个位置heaptifyUp(arr, (*size) - 1); // 重新调整堆
}
void popHeap(float *arr, int *size, float *val) {if ((*size) <= 0) return; // 如果堆为空,直接返回*val = arr[0]; arr[0] = arr[(*size) - 1]; // 将堆的最后一个元素放在堆顶(*size)--; heaptifyDown(arr, *size, 0); // 重新调整堆
}
void buildHeap(float *arr, int size){// 使用heaptifyDown的原因是,如果使用 heaptifyUp从树的底部向上调整,每个节点在最坏情况下可能需要一直移到树的根部(并且底部的节点数量多)。// 这意味着可能需要执行更多的比较和交换操作。而如果我们从上往下调整,每个节点最多只需要向下移动几层(通常是树的高度),这使得整体效率非常高。for (int i = size / 2 - 1; i >= 0; i--) heaptifyDown(arr, size, i); // 从最后一个非叶子节点(size/2 - 1)开始,向上调整堆;
}
void freeHeap(float **arr) { if (*arr) { free(*arr); *arr = NULL; } }
void top_k(float *arr, int size, float *res, int k) {if (k <= 0 || k > size) return;float *heap = (float *)malloc(sizeof(float) * k); int heap_size = 0;for (int i = 0; i < k; i++){ pushHeap(heap, &heap_size, arr[i]); } //  // 初始化堆,放入前 k 个元素for (int i = k; i < size; i++){ if (arr[i] > heap[0]) { float tmp; popHeap(heap, &heap_size, &tmp); pushHeap(heap, &heap_size, arr[i]); } } // 处理剩余的元素for (int i = 0; i < k; i++) { popHeap(heap, &heap_size, &res[k - 1 - i]); } // 将堆中的元素按降序输出到 res 数组freeHeap(&heap);
}
// parallel: 在构建堆时,并行化处理多个子树的下沉操作
void heap_sort(float *heap, int size){ // 堆排序,从大道小排序buildHeap( heap, size); // 构建小顶堆while (size > 1) {float tmp = heap[0]; heap[0] = heap[size - 1]; heap[size - 1] = tmp; size--; // 将堆顶元素与堆的最后一个元素交换,每次循环最小的元素被调整到末尾,堆的大小减1heaptifyDown(heap, size, 0); // 从堆顶开始重新调整堆}freeHeap(&heap);
} 大顶堆
//void initHeap(float **arr, int *size) { *arr = (float *)malloc(sizeof(float) * MAX_SIZE); *size = 0; } // 初始化堆的大小为0
//void peekTop(float *arr, int size, float *x) { if (size <= 0) return ; *x = arr[0]; }
//void getParent(int i, int *p_idx) { *p_idx = (i - 1) / 2; }
//void getLeftChild(int i, int *lc_idx) { *lc_idx = 2 * i + 1; }
//void getRightChild(int i, int *rc_idx) { *rc_idx = 2 * i + 2; }
//void heaptifyUp(float *arr, int idx){
//    int cur = idx;
//    while (cur > 0){  // 只要 cur 不是根节点
//        int par; getParent(cur, &par); if (arr[cur] < arr[par]) break; // 如果当前节点小于父节点,退出循环
//        float tmp = arr[cur]; arr[cur] = arr[par]; arr[par] = tmp; cur = par;  // 交换当前节点和父节点
//    }
//}
//void heaptifyDown(float *arr, int size, int idx){
//    int cur = idx;
//    while (true){
//        int lc, rc, maxidx = cur; getLeftChild(cur, &lc); getRightChild(cur, &rc);
//        if (lc < size && arr[lc] > arr[maxidx]) { maxidx = lc;} // 找到当前节点和左子节点中的最大值
//        if (rc < size && arr[rc] > arr[maxidx]) { maxidx = rc;} // 找到当前节点和右子节点中的最大值
//        if (maxidx == cur) break; // 如果当前节点是最大值,退出循环
//        float tmp = arr[cur]; arr[cur] = arr[maxidx]; arr[maxidx] = tmp; cur = maxidx; // 交换当前节点和最大值节点
//    }
//}
//void pushHeap(float *arr, int *size, float x){
//    if ((*size) >= MAX_SIZE) return; // 如果堆已满,直接返回
//    arr[*size] = x; (*size)++; // 将新元素放在堆的最后一个位置
//    heaptifyUp(arr, (*size) - 1); // 重新调整堆
//}
//void popHeap(float *arr, int *size, float *val){
//    if ((*size) <= 0) return; // 如果堆为空,直接返回
//    *val = arr[0];
//    arr[0] = arr[(*size) - 1]; // 将堆的最后一个元素放在堆顶
//    (*size)--;
//    heaptifyDown(arr, *size,0); // 重新调整堆
//}
//void freeHeap(float **arr) { if (*arr) { free(*arr); *arr = NULL; } }

这篇关于堆的建立、插入、出堆、堆化、topk问题、堆排序(C语言实现)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131599

相关文章

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

C语言中%zu的用法解读

《C语言中%zu的用法解读》size_t是无符号整数类型,用于表示对象大小或内存操作结果,%zu是C99标准中专为size_t设计的printf占位符,避免因类型不匹配导致错误,使用%u或%d可能引发... 目录size_t 类型与 %zu 占位符%zu 的用途替代占位符的风险兼容性说明其他相关占位符验证示

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3

解决RocketMQ的幂等性问题

《解决RocketMQ的幂等性问题》重复消费因调用链路长、消息发送超时或消费者故障导致,通过生产者消息查询、Redis缓存及消费者唯一主键可以确保幂等性,避免重复处理,本文主要介绍了解决RocketM... 目录造成重复消费的原因解决方法生产者端消费者端代码实现造成重复消费的原因当系统的调用链路比较长的时