堆与堆排序及 Top-K 问题解析:从原理到实践

news2025/6/3 10:41:08

一、堆的本质与核心特性

堆是一种基于完全二叉树的数据结构,其核心特性为父节点与子节点的数值关系,分为大堆和小堆两类:

 

  • 大堆:每个父节点的值均大于或等于其子节点的值,堆顶元素为最大值。如:

  • 小堆:每个父节点的值均小于或等于其子节点的值,堆顶元素为最小值。

这种特性使得堆能够高效地支持插入删除堆顶元素获取极值等操作,时间复杂度均为 \(O(\log n)\),其中 n 为堆中元素个数。

二、堆排序:基于堆的经典排序算法

(一)算法思想

堆排序的核心是利用堆的特性实现排序,分为两个阶段:

  1. 建堆阶段:将初始序列构建成一个大堆(或小堆)。
  2. 排序阶段:依次将堆顶元素与末尾元素交换,调整剩余元素维持堆结构,直至排序完成。

(二)实现步骤(以小堆为例)

假设现有数组 data = { 4,2,8,1,5,6,9,7,2,7,9},如图

其排序过程如下:

  1. 构建小堆
    • 从最后一个非叶子节点(索引为(n-1-1) / 2)开始调整。
    • 运用向下调整算法进行处理。
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
    int child = parent * 2 + 1;
    while(child < n)//找不到孩子
    {
        //找出小的孩子
        if(child + 1 < n && a[child + 1] < a[child])
        {
            child = child + 1;
        }
        //调整
        if(a[child] < a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

 

  1. 排序过程
    • 交换堆顶与末尾元素,此时末尾元素为最小值,固定。
    • 对前 n - 1 个元素重新调整为小堆。
    • 重复交换和调整操作,最终得到有序数组 {9 9 8 7 7 6 5 4 2 2 1}。

 

(三)代码实现(C)

//堆排序(O(N*logN))
void HeapSort(int* a, int n)
{
    //降序,建小堆
    //升序,建大堆
    
    // for(int i = 1; i < n; i++)
    // {
    //     AdjustUp(a, i);//向上调整建堆(O(N*logN))
    // }
    for(int i = (n-1-1)/2; i >= 0; i--)
    {
        AdjustDown(a, n, i);//向下调整建堆(O(N))
    }

    int end = n - 1;
    //(O(N*logN))
    while(end > 0)
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);
        --end;
    }

    for(size_t i = 0; i < n; i++)
    {
        printf("%d ",a[i]);
    }
    printf("\n");
}

三、Top-K 问题:基于堆的高效求解方案

(一)问题定义

在海量数据中快速找到最大的 K 个数(或最小的 K 个数),例如从 100 万个数中找出前 10 大的数。

(二)核心思路

  • 求最大的 K 个数:使用小堆,堆中维护当前最大的 K 个数。遍历数据时,若当前数大于堆顶(堆中最小值),则替换堆顶并调整堆。
  • 求最小的 K 个数:使用大堆,堆中维护当前最小的 K 个数。遍历数据时,若当前数小于堆顶(堆中最大值),则替换堆顶并调整堆。

(三)代码实现(C,求最大的 K 个数)

假设文件 data.txt 中存储了大量整数,可通过以下步骤进行Top-K 求解:

  1. 输入数据:将数据输入进data.txt中
  2. Top-K 查询:调用 topK 函数获取前 K 大或前 K 小的元素。
void CreateNData()
{
    //造数据
    int n = 100000;
    srand((unsigned int)time(NULL));
    const char* file = "data.txt";
    FILE* fin = fopen(file, "w");
    if(fin == NULL)
    {
        perror("fopen fail!");
        return;
    }

    for(int i = 0; i < n; i++)
    {
        int x = (rand()+i) % 1000000;
        fprintf(fin, "%d\n", x);
    }
    fclose(fin);
}

void Test03()
{/* A */
    //读前k个数据
    system("chcp 65001");
    int k = 0;
    printf("请输出k>:");
    scanf("%d",&k);
    int* kminheap = (int*)malloc(k*sizeof(int));
    if(kminheap == NULL)
    {
        perror("malloc fail!");
        return;
    }
    const char* file = "data.txt";
    FILE* fout = fopen(file, "r");
    if(fout == NULL)
    {
        perror("fopen fail!");
        exit(1);
    }
    for(int i = 0; i < k; i++)
    {
        fscanf(fout, "%d", &kminheap[i]);
    }
    //建小堆
    for(int i = (k-1-1)/2; i >= 0; i--)
    {
        AdjustDown(kminheap, k, i);
    }
    //读取剩下N-K个数
    int x = 0;
    while(fscanf(fout, "%d", &x) > 0)
    {
        if(x > kminheap[0])
        {
            kminheap[0] = x;
            AdjustDown(kminheap, k, 0);
        }
    }
    //打印topk
    printf("最大前%d个数",k);
    for(int i = 0; i < k; i++)
    {
        printf("%d ", kminheap[i]);
    }
    printf("\n");
}

四、性能分析与应用场景

(一)时间复杂度

  • 堆排序:建堆时间为 ,每次调整时间为 (O(log n)),总时间复杂度为 (O(nlog n)),属于不稳定排序。
  • Top-K 问题:遍历数据时间为 (O(n)),每次堆操作时间为 (O(\log K)),总时间复杂度为 (O(nlog K)),适用于 (K 远小于 n) 的场景。

(二)应用场景

  • 堆排序:适用于内存中数据排序,尤其适合不需要额外空间的原地排序(空间复杂度 (O(1)))。
  • Top-K 问题
    • 海量日志分析:提取热门访问记录。
    • 数据挖掘:找出频繁出现的元素。
    • 实时系统:实时监控数据中的极值点。

五、总结

堆作为一种高效的数据结构,通过巧妙的完全二叉树性质实现了快速的极值操作。堆排序和 Top-K 问题是其典型应用,前者利用建堆和交换实现排序,后者通过维护固定大小的堆解决海量数据中的极值查询。理解堆的核心原理与应用,能有效提升数据处理的效率,尤其在面对大规模数据时优势显著。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2394860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件锁:守护隐私,安心无忧

数字化时代&#xff0c;手机已成为我们生活中不可或缺的一部分&#xff0c;它不仅存储着我们的个人信息、照片、聊天记录等重要数据&#xff0c;还承载着我们的社交、娱乐和工作等多种功能。然而&#xff0c;这也意味着手机上的隐私信息面临着诸多泄露风险。无论是家人、朋友还…

无人机桥梁3D建模、巡检、检测的航线规划

无人机桥梁3D建模、巡检、检测的航线规划 无人机在3D建模、巡检和检测任务中的航线规划存在显著差异&#xff0c;主要体现在飞行高度、航线模式、精度要求和传感器配置等方面。以下是三者的详细对比分析&#xff1a; 1. 核心目标差异 任务类型主要目标典型应用场景3D建模 生成…

【Java基础05】面向对象01

文章目录 1. 设计对象并使用1.1 类与对象1.2 封装1.2.1 private关键字1.2.2 this关键字成员变量和局部变量的区别 1.2.3 构造方法1.2.4 标准JavaBean类 1.3 对象内存图 本文部分参考这篇博客 1. 设计对象并使用 1.1 类与对象 public class 类名{1、成员变量(代表属性,一般是名…

设计模式:观察者模式 - 实战

一、观察者模式场景 1.1 什么是观察者模式&#xff1f; 观察者模式&#xff08;Observer Pattern&#xff09;观察者模式是一种行为型设计模式&#xff0c;用于定义一种一对多的依赖关系&#xff0c;当对象的状态发生变化时&#xff0c;所有依赖于它的对象都会自动收到通知并更…

YOLOv8 移动端升级:借助 GhostNetv2 主干网络,实现高效特征提取

文章目录 引言GhostNetv2概述GhostNet回顾GhostNetv2创新 YOLOv8主干网络改进原YOLOv8主干分析GhostNetv2主干替换方案整体架构设计关键模块实现 完整主干网络实现YOLOv8集成与训练模型集成训练技巧 性能对比与分析计算复杂度对比优势分析 部署优化建议结论与展望 引言 目标检…

国产化Word处理控件Spire.Doc教程:在 C# 中打印 Word 文档终极指南

在 C# 中以编程方式打印 Word 文档可以简化业务工作流程、自动化报告和增强文档管理系统。本指南全面探讨如何使用Spire.Doc for .NET打印 Word 文档&#xff0c;涵盖从基本打印到高级自定义技术的所有内容。我们将逐步介绍每种情况下的实际代码示例&#xff0c;确保您能够在实…

谷歌:贝叶斯框架优化LLM推理反思

&#x1f4d6;标题&#xff1a;Beyond Markovian: Reflective Exploration via Bayes-Adaptive RL for LLM Reasoning &#x1f310;来源&#xff1a;arXiv, 2505.20561 &#x1f31f;摘要 通过强化学习 (RL) 训练的大型语言模型 (LLM) 表现出强大的推理能力和紧急反射行为&a…

Qt SQL模块基础

Qt SQL模块基础 一、Qt SQL模块支持的数据库 官方帮助文档中的Qt支持的数据库驱动如下图&#xff1a; Qt SQL 模块中提供了一些常见的数据库驱动&#xff0c;包括网络型数据库&#xff0c;如Qracle、MS SQL Server、MySQL等&#xff0c;也包括简单的单机型数据库。 Qt SQL支…

[9-3] 串口发送串口发送+接收 江协科技学习笔记(26个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26中断

如何在Qt中绘制一个带有动画的弧形进度条?

如何在Qt中绘制一个弧形的进度条 在图形用户界面开发中&#xff0c;进度指示控件&#xff08;Progress Widget&#xff09;是非常常见且实用的组件。CCArcProgressWidget 是一个继承自 QWidget 的自定义控件&#xff0c;用于绘制圆弧形进度条。当然&#xff0c;笔者看了眼公开…

国产三维CAD皇冠CAD(CrownCAD)建模教程:汽车电池

在线解读『汽车电池』的三维建模流程&#xff0c;讲解3D草图、保存实体、拉伸凸台/基体、设置外观等操作技巧&#xff0c;一起和皇冠CAD&#xff08;CrownCAD&#xff09;学习制作步骤吧&#xff01; 汽车电池&#xff08;通常指铅酸蓄电池或锂离子电池&#xff09;是车辆电气系…

VMware-workstation安装教程--超详细(附带安装包)附带安装CentOS系统教程

VMware-workstation安装教程--超详细&#xff08;附带安装包&#xff09;附带安装CentOS系统教程 一、下载软件VMwware二、下载需要的镜像三、在VMware上安装系统 一、下载软件VMwware 二、下载需要的镜像 三、在VMware上安装系统 VMware 被 Broadcom&#xff08;博通&#x…

2025年- H63-Lc171--33.搜索旋转排序数组(2次二分查找,需二刷)--Java版

1.题目描述 2.思路 输入&#xff1a;旋转后的数组 nums&#xff0c;和一个整数 target 输出&#xff1a;target 在 nums 中的下标&#xff0c;如果不存在&#xff0c;返回 -1 限制&#xff1a;时间复杂度为 O(log n)&#xff0c;所以不能用遍历&#xff0c;必须使用 二分查找…

3D-激光SLAM笔记

目录 定位方案 编译tbb ros2humble安装 命令 colcon commond not found 栅格地图生成&#xff1a; evo画轨迹曲线 安装gtsam4.0.2 安装ceres-solver1.14.0 定位方案 1 方案一&#xff1a;改动最多 fasterlio 建图&#xff0c;加闭环优化&#xff0c;参考fast-lio增加关…

HomeKit 基本理解

概括 HomeKit 将用户的家庭自动化信息存储在数据库中&#xff0c;该数据库由苹果的内置iOS家庭应用程序、支持HomeKit的应用程序和其他开发人员的应用程序共享。所有这些应用程序都使用HomeKit框架作为对等程序访问数据库. Home 只是相当于 HomeKit 的表现层,其他应用在实现 …

(LeetCode 每日一题) 909. 蛇梯棋 (广度优先搜索bfs)

题目&#xff1a;909. 蛇梯棋 思路&#xff1a;广度优先搜索bfs队列&#xff0c;时间复杂度0(6*n^2)。 细节看注释 C版本&#xff1a; class Solution { public:int snakesAndLadders(vector<vector<int>>& board) {int nboard.size();// vis[i]&#xff1a;…

生成https 证书步骤

一、OpenSSL下载 OpenSSL下载地址&#xff1a; https://slproweb.com/products/Win32OpenSSL.html 如果电脑是64位的就选择64位的 二、OpenSSL安装 双击打开.exe文件 开始安装&#xff0c;一直下一步&#xff0c;不过需要注意的是默认安装路径是C盘&#xff0c;可更改到其他盘…

设计模式——适配器设计模式(结构型)

摘要 本文详细介绍了适配器设计模式&#xff0c;包括其定义、核心思想、角色、结构、实现方式、适用场景及实战示例。适配器模式是一种结构型设计模式&#xff0c;通过将一个类的接口转换成客户端期望的另一个接口&#xff0c;解决接口不兼容问题&#xff0c;提高系统灵活性和…

小黑大语言模型通过设计demo进行应用探索:langchain中chain的简单理解demo

chain简介 LangChain 中的 Chain 模块‌在开发大型语言模型&#xff08;LLM&#xff09;驱动的应用程序中起着至关重要的作用。Chain是串联LLM能力与实际业务的关键桥梁&#xff0c;通过将多个工具和模块按逻辑串联起来&#xff0c;实现复杂任务的多步骤流程编排。 案例 通过…

秒杀系统—5.第二版升级优化的技术文档三

大纲 8.秒杀系统的秒杀库存服务实现 9.秒杀系统的秒杀抢购服务实现 10.秒杀系统的秒杀下单服务实现 11.秒杀系统的页面渲染服务实现 12.秒杀系统的页面发布服务实现 8.秒杀系统的秒杀库存服务实现 (1)秒杀商品的库存在Redis中的结构 (2)库存分片并同步到Redis的实现 (3…