深入理解C/C++堆数据结构:从原理到实战

news2025/3/16 14:57:06

一、堆的本质与特性

1.1 什么是堆数据结构?

堆(Heap)是一种特殊的完全二叉树,它满足以下核心性质:

  • 堆序性:每个节点的值都满足特定顺序关系

  • 结构性:完全二叉树的结构特性(除最后一层外都是满的,最后一层节点左对齐)

这种数据结构由J.W.J. Williams在1964年提出,最初用于实现高效的堆排序算法。堆在计算机科学中的应用非常广泛,从操作系统内存管理到高级算法实现都有它的身影。

1.2 堆的两种基本类型

最大堆(Max-Heap)

  • 父节点值 ≥ 子节点值

  • 根节点是整个堆的最大值

  • 典型应用:优先队列

最小堆(Min-Heap)

  • 父节点值 ≤ 子节点值

  • 根节点是整个堆的最小值

  • 典型应用:定时任务调度

1.3 堆的底层实现方式

堆通常使用数组实现,其数组索引与树结构存在精确的数学对应关系:

数组实现的优势:

  • 内存连续,缓存友好

  • 索引计算高效(位运算优化)

  • 完全二叉树的天然适配性

二、堆的核心操作与实现

2.1 元素插入(上浮操作)

插入操作的算法步骤:

  1. 将新元素添加到数组末尾

  2. 自底向上调整堆结构(上浮)

  3. 时间复杂度:O(log n)

    void MaxHeap::insert(int key) {
        if (size >= capacity) {
            resize(2 * capacity);//扩容操作
        }
        
        heap[size] = key;
        int current = size++;
    
         // 上浮操作-大堆
        while (current != 0 && heap[parent(current)] < heap[current]) {
            //parent(current)对父亲节点的索引
            swap(heap[current], heap[parent(current)]);
            current = parent(current);
        }
    
    }

    2.2 元素删除(下沉操作)

    删除根节点的算法步骤:

    1. 用最后一个元素替换根节点

    2. 自顶向下调整堆结构(下沉)

    3. 时间复杂度:O(log n)

      int MaxHeap::extractMax() {
          if (size <= 0)
              return INT_MIN;
          if (size == 1)
              return heap[--size];
          
          int root = heap[0];
          heap[0] = heap[--size];
          maxHeapify(0);
          
          return root;
      }
      
      void MaxHeap::maxHeapify(int i) {//递归调用
          int l = left(i);//索引左孩子下标
          int r = right(i);//索引右孩子下标
          int largest = i;
          
          if (l < size && heap[l] > heap[i])
              largest = l;
          if (r < size && heap[r] > heap[largest])
              largest = r;
          
          if (largest != i) {
              swap(heap[i], heap[largest]);
              maxHeapify(largest);
          }
      }

      2.3 堆的构建

      两种建堆方式对比:

      方法时间复杂度适用场景
      连续插入法O(n log n)动态数据流
      Floyd算法O(n)静态数组初始化

      Floyd算法的实现技巧:

      void MaxHeap::buildHeap() {
          // 从最后一个非叶子节点开始调整
          int startIdx = (size/2) - 1;
          
          for (int i = startIdx; i >= 0; i--) {
              maxHeapify(i);
          }
      }

三、C/C++中的堆实现

3.1 手动实现堆结构(大堆)

#include <iostream>
#include <stdexcept>
#include <algorithm>

template <typename T>
class MaxHeap {
private:
    T* heapArray;     // 堆存储数组
    int capacity;     // 数组容量
    int currentSize;  // 当前元素数量

    // 计算父节点索引
    inline int parent(int i) const { return (i-1) >> 1; } // 用位运算优化除法

    // 计算左子节点索引
    inline int leftChild(int i) const { return (i << 1) + 1; }

    // 计算右子节点索引
    inline int rightChild(int i) const { return (i << 1) + 2; }

    // 动态扩容(倍增策略)
    void resize() {
        int newCapacity = capacity == 0 ? 1 : capacity * 2;
        T* newArray = new T[newCapacity];
        
        // 迁移数据
        for (int i = 0; i < currentSize; ++i) {
            newArray[i] = heapArray[i];
        }
        
        delete[] heapArray;
        heapArray = newArray;
        capacity = newCapacity;
    }

    // 上浮操作
    void siftUp(int index) {
        while (index > 0 && heapArray[parent(index)] < heapArray[index]) {
            std::swap(heapArray[parent(index)], heapArray[index]);
            index = parent(index);
        }
    }

    // 下沉操作
    void siftDown(int index) {
        int maxIndex = index;
        int left = leftChild(index);
        int right = rightChild(index);

        // 找出三个节点中的最大值
        if (left < currentSize && heapArray[left] > heapArray[maxIndex]) {
            maxIndex = left;
        }
        if (right < currentSize && heapArray[right] > heapArray[maxIndex]) {
            maxIndex = right;
        }

        // 如果最大值不是当前节点,交换并递归调整
        if (maxIndex != index) {
            std::swap(heapArray[index], heapArray[maxIndex]);
            siftDown(maxIndex);
        }
    }

public:
    // 构造函数
    explicit MaxHeap(int initialCapacity = 10) 
        : capacity(initialCapacity), currentSize(0) {
        if (initialCapacity <= 0) {
            throw std::invalid_argument("Initial capacity must be positive");
        }
        heapArray = new T[capacity];
    }

    // 从数组构建堆(Floyd算法)
    MaxHeap(T arr[], int size) : currentSize(size) {
        capacity = size == 0 ? 1 : size;
        heapArray = new T[capacity];
        
        // 拷贝数据
        for (int i = 0; i < size; ++i) {
            heapArray[i] = arr[i];
        }
        
        // 从最后一个非叶子节点开始调整
        for (int i = (size/2)-1; i >= 0; --i) {
            siftDown(i);
        }
    }

    // 析构函数
    ~MaxHeap() {
        delete[] heapArray;
    }

    // 插入元素
    void insert(T value) {
        if (currentSize == capacity) {
            resize();
        }
        
        heapArray[currentSize] = value;
        siftUp(currentSize);
        ++currentSize;
    }

    // 提取最大值
    T extractMax() {
        if (isEmpty()) {
            throw std::out_of_range("Heap is empty");
        }
        
        T max = heapArray[0];
        heapArray[0] = heapArray[--currentSize];
        siftDown(0);
        return max;
    }

    // 获取最大值(不删除)
    T getMax() const {
        if (isEmpty()) {
            throw std::out_of_range("Heap is empty");
        }
        return heapArray[0];
    }

    // 堆是否为空
    bool isEmpty() const {
        return currentSize == 0;
    }

    // 堆排序(会破坏堆结构)
    static void heapSort(T arr[], int n) {
        // 构建最大堆
        for (int i = n/2 - 1; i >= 0; --i) {
            MaxHeap<T>::siftDown(arr, n, i);
        }
        
        // 逐个提取元素
        for (int i = n-1; i > 0; --i) {
            std::swap(arr[0], arr[i]);
            MaxHeap<T>::siftDown(arr, i, 0);
        }
    }

    // 打印堆内容(调试用)
    void print() const {
        std::cout << "[";
        for (int i = 0; i < currentSize; ++i) {
            std::cout << heapArray[i];
            if (i != currentSize-1) std::cout << ", ";
        }
        std::cout << "]" << std::endl;
    }

private:
    // 静态方法用于堆排序
    static void siftDown(T arr[], int n, int i) {
        int maxIndex = i;
        int left = 2*i + 1;
        int right = 2*i + 2;

        if (left < n && arr[left] > arr[maxIndex]) {
            maxIndex = left;
        }
        if (right < n && arr[right] > arr[maxIndex]) {
            maxIndex = right;
        }

        if (maxIndex != i) {
            std::swap(arr[i], arr[maxIndex]);
            siftDown(arr, n, maxIndex);
        }
    }
};

// 测试用例
int main() {
    try {
        // 测试1:基本插入和提取
        MaxHeap<int> heap;
        heap.insert(3);
        heap.insert(1);
        heap.insert(4);
        heap.insert(1);
        heap.insert(5);
        
        std::cout << "Test 1:" << std::endl;
        heap.print(); // [5, 4, 3, 1, 1]
        while (!heap.isEmpty()) {
            std::cout << heap.extractMax() << " ";
        } // 5 4 3 1 1
        std::cout << "\n\n";

        // 测试2:从数组构建堆
        int arr[] = {2,7,4,1,8,1};
        MaxHeap<int> heap2(arr, 6);
        std::cout << "Test 2:" << std::endl;
        heap2.print(); // [8, 7, 4, 1, 2, 1]
        std::cout << "Max: " << heap2.getMax() << "\n\n"; // 8

        // 测试3:堆排序
        int sortArr[] = {9,3,2,5,1,4,8};
        const int n = sizeof(sortArr)/sizeof(sortArr[0]);
        MaxHeap<int>::heapSort(sortArr, n);
        std::cout << "Test 3 (Heap Sort):" << std::endl;
        for (int i = 0; i < n; ++i) {
            std::cout << sortArr[i] << " "; // 1 2 3 4 5 8 9
        }
        std::cout << "\n\n";

        // 测试4:异常处理
        MaxHeap<int> emptyHeap;
        try {
            emptyHeap.extractMax();
        } catch (const std::exception& e) {
            std::cout << "Test 4 Exception: " << e.what() << std::endl;
        }
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

3.2 STL中的priority_queue

标准库的使用示例:

#include <queue>

// 最小堆
priority_queue<int, vector<int>, greater<int>> minHeap;

// 最大堆(默认)
priority_queue<int> maxHeap;

// 自定义比较函数
struct Compare {
    bool operator()(const Person& a, const Person& b) {
        return a.age > b.age; // 年龄小的优先
    }
};
priority_queue<Person, vector<Person>, Compare> customHeap;

四、堆的高级应用

4.1 堆排序算法

实现步骤:

  1. 构建最大堆

  2. 重复提取根节点并调整堆

  3. 时间复杂度:O(n log n)

    void heapSort(int arr[], int n) {
        // 构建堆
        for (int i = n/2 - 1; i >= 0; i--)
            heapify(arr, n, i);
        
        // 逐个提取元素
        for (int i = n-1; i > 0; i--) {
            swap(arr[0], arr[i]);
            heapify(arr, i, 0);
        }
    }

    总结:升序建大堆,降序建小堆

4.2 海量数据Top K问题

使用堆的高效解法:

vector<int> getTopK(vector<int>& nums, int k) {
    priority_queue<int, vector<int>, greater<int>> minHeap;
    
    for (int num : nums) {
        if (minHeap.size() < k) {
            minHeap.push(num);
        } else if (num > minHeap.top()) {
            minHeap.pop();
            minHeap.push(num);
        }
    }
    
    vector<int> result;
    while (!minHeap.empty()) {
        result.push_back(minHeap.top());
        minHeap.pop();
    }
    return result;
}

 五、堆的工程实践要点(了解)

5.1 内存管理最佳实践

  • 动态数组扩容策略(倍增法)

  • 异常安全处理

  • 移动语义优化(C++11+)

5.2 性能优化技巧

  • 缓存友好的内存布局

  • 循环展开优化heapify

  • 预分配内存策略

  • 位运算优化索引计算

5.3 常见陷阱与调试技巧

  1. 数组越界问题

  2. 比较逻辑错误

  3. 内存泄漏检测

  4. 堆属性验证函数:

    bool isMaxHeap(int arr[], int n, int i = 0) {
        if (i >= n) return true;
        
        int l = 2*i + 1;
        int r = 2*i + 2;
        
        bool valid = true;
        if (l < n) valid &= (arr[i] >= arr[l]);
        if (r < n) valid &= (arr[i] >= arr[r]);
        
        return valid && isMaxHeap(arr, n, l) && isMaxHeap(arr, n, r);
    }

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

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

相关文章

【MATLAB实战】实现白鲸算法(BWO)优化BP神经网络:提升模型性能的新思路

一、什么是白鲸优化算法&#xff08;BWO&#xff09;&#xff1f; 白鲸优化算法是受自然界中白鲸群体行为和觅食策略启发的一种新型智能优化算法。白鲸在捕食过程中展现出高效的协作能力和适应性&#xff0c;例如通过“回声定位”搜索猎物位置群体间信息共享&#xff0c;这些行…

医疗资源联动,广州长泰医院与海南德雅医院共筑地贫防治新篇章

​ 为贯彻落实"健康中国"战略关于出生缺陷综合防治的部署要求&#xff0c;推动地中海贫血防治体系建设。2025年3月15日&#xff0c;广州长泰医院与海南德雅医院联合主办的“地中海贫血生殖遗传干预大型义诊暨合作签约仪式”在广州正式启动&#xff0c;活动以“爱与希…

DeepSeek在医学领域的应用

DeepSeek作为高性能AI大模型&#xff0c;在医学领域的应用场景广泛&#xff0c;结合其在数据处理、自然语言理解和深度学习方面的优势&#xff0c;显著推动了医疗行业的智能化转型。以下是其核心应用场景及具体案例&#xff1a; 1. 辅助诊断与决策支持 临床辅助诊断&#xff1…

3.数据结构-串、数组和广义表

串、数组和广义表 3.1串3.1.1串的类型定义、存储结构及其运算串的顺序存储串的堆式顺序存储结构串的链式存储 3.1.2 串的模式匹配算法BF算法*KMP算法&#xff08;待更新&#xff09; 3.2数组3.2.1数组的顺序存储3.2.2特殊矩阵的压缩存储对称矩阵三角矩阵对角矩阵 3.3广义表*案例…

苹果电脑杀毒软件CleanMyMac

杀毒软件在苹果家族中是一个小众软件&#xff0c;百度搜索苹果电脑杀毒软件&#xff0c;可能各种杀软良莠不齐&#xff0c;因为在这个市场非常小&#xff0c;绝大多数都是冲着“清理”去的&#xff0c;而不是杀毒。最近测试了一款Mac电脑杀毒软件&#xff0c;杀毒效果也是一般般…

Day16:二叉搜索树和双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 。 对于双向循环列表&#xff0c;你可以将左右孩子指针作为双向循环链表的前驱和后继指针&#xff0c;第一个节点的前驱是最后一个节点&#xff0c;最后一个节点的后继是第一个节点。 特别地&#xff0c;我们希望可以…

Qt QML实现弹球消砖块小游戏

前言 弹球消砖块游戏想必大家都玩过&#xff0c;很简单的小游戏&#xff0c;通过移动挡板反弹下落的小球&#xff0c;然后撞击砖块将其消除。本文使用QML来简单实现这个小游戏。 效果图&#xff1a; 正文 代码目录结构如下&#xff1a; 首先是小球部分&#xff0c;逻辑比较麻…

如何在实际应用中测量和调整直线导轨的预紧力?

在实际应用中&#xff0c;准确测量和调整直线导轨的预紧力对于保证设备的性能和精度至关重要&#xff0c;但测量和调整直线导轨的预紧力需要根据具体的导轨型号和结构来选择合适的方法。以下是一些常见的测量和调整方法&#xff1a; 1、使用压力传感器&#xff1a;在一些先进的…

YOLO11 使用入门

YOLO12 使用入门 1. 源码下载2. 权重下载3. 环境配置4. 例程测试4.1. 目标检测4.1.1. 源文件 model4.1.2. 结果Results4.1.3. 边界框 Boxes 2.2. 图像分割4.2.1. 推理 model.predict4.2.2. 掩码 Masks 1. 源码下载 之前介绍了《目标检测 YOLOv5 使用入门》 现在是 2024.12.2…

汽车感性负载-智能高边钳位能量计算

随着汽车电子技术的发展&#xff0c;新的电子电气架构下&#xff0c;越来越多的执行部件在车身出现&#xff0c;比如电磁阀、风机、水泵、油泵、雨刮继电器等常用的执行器&#xff0c; 它们一般都表现为感性特点。驱动这些负载的最简单和最常见的方法是将它们连接到高边侧开关(…

基于Python+Flask+MySQL+HTML的爬取豆瓣电影top-250数据并进行可视化的数据可视化平台

FlaskMySQLHTML 项目采用前后端分离技术&#xff0c;包含完整的前端&#xff0c;以flask作为后端 Pyecharts、jieba进行前端图表展示 通过MySQL收集格列数据 通过Pyecharts制作数据图表 这是博主b站发布的详细讲解&#xff0c;感兴趣的可以去观看&#xff1a;【Python爬虫可…

七大常用智能家居协议对比

如果您不知道在项目中使用哪种智能家居通信协议&#xff0c;那么进入智能家居行业可能会很困难。如果没有合适的协议将其集成到智能家居生态系统中&#xff0c;智能家居设备将无法正常工作。否则&#xff0c;您将面临硬件和软件无法满足最终用户期望的风险。协议选择不当可能会…

996引擎-问题处理:缺失特效分割文件 ModelAtlasSplitConfigs

通常我们买的资源都是带会 ModelAtlasSplitConfigs.txt 或 sceneAtlasSplitConfigs.txt 的 但有时确实找不到的话&#xff0c;是可以用996工具生成的&#xff1a;

异步加载错误如何解决

首先是 提供两张图 如果数据过多的情况下我在所内和住家形式频繁的来回切换 导致数据展示的不一样 大家是不是有这样的问题 这个是导致了数据展示有问题的情况 住家的情况本来是没有几层的 下面我帮大家解决一下 // 防止异步延迟 const Noop () > { } const lhl (resDa…

R语言零基础系列教程-01-R语言初识与学习路线

代码、讲义、软件回复【R语言01】获取。 R语言初识 R是一个开放的统计编程环境&#xff0c;是一门用于统计计算和作图的语言。“一切皆是对象”&#xff0c;数据、函数、运算符、环境等等都是对象。易学&#xff0c;代码像伪代码一样简洁&#xff0c;可读性高强大的统计和可视…

自动化测试-网页聊天室

项目介绍&#xff1a; 针对基于WebSocket协议的网页端即时通讯系统&#xff0c;主导设计并实施全流程自动化测试方案。通过构建模块化测试框架&#xff0c;完成对核心业务场景&#xff08;用户登录鉴权、消息同步、实时聊天等&#xff09;的自动化验证&#xff0c;最终达成测试…

创新实践分享:基于边缘智能+扣子的智能取物机器人解决方案

在 2024 年全国大学生物联网设计竞赛中&#xff0c;火山引擎作为支持企业&#xff0c;不仅参与了赛道的命题设计&#xff0c;还为参赛队伍提供了相关的硬件和软件支持。以边缘智能和扣子的联合应用为核心&#xff0c;参赛者们在这场竞赛中展现出了卓越的创新性和实用性&#xf…

【蓝桥杯】省赛:神奇闹钟

思路 python做这题很简单&#xff0c;灵活用datetime库即可 code import os import sys# 请在此输入您的代码 import datetimestart datetime.datetime(1970,1,1,0,0,0) for _ in range(int(input())):ls input().split()end datetime.datetime.strptime(ls[0]ls[1],&quo…

使用kubeadm方式以及使用第三方工具sealos搭建K8S集群

目录 kubeadm方式: 一、安装要求 二、环境准备 二、安装Docker、kubeadm、kubelet 1、安装Docker &#xff08;1&#xff09;首先配置一下Docker的阿里yum源 &#xff08;2&#xff09;然后用yum 方式安装docker &#xff08;3&#xff09;配置Docker镜像加速器 &#…

2025 linux系统资源使用率统计docker容器使用率统计docker监控软件Weave Scope安装weavescope

1.Weave Scope介绍 Weave Scope 是一款用于监控和可视化 Docker 容器、Kubernetes 集群以及分布式应用的强大工具。它的设计目标是帮助开发者和运维人员更好地理解和管理复杂的微服务架构。以下是 Weave Scope 的主要优点&#xff1a; 1. 实时可视化 Weave Scope 提供了一个直…