当前位置: 首页 > news >正文

【数据结构】非线性数据结构——堆

1. 堆的介绍

堆(Heap) 是一种 完全二叉树,满足某种特定的顺序性质:

  • 大根堆(Max Heap):任意节点的值 ≥ 子节点。

  • 小根堆(Min Heap):任意节点的值 ≤ 子节点。

堆的根节点永远是堆中的最大值(大根堆)或最小值(小根堆),堆通常用 数组 来存储,本质是 “用数组存储的二叉树”,完全二叉树结构保证了堆可以用数组紧凑存储(无冗余空间),同时通过数组索引的数学关系可以模拟二叉树的父子节点关联,避免了链式存储的指针开销。

2. 堆的存储方式

假设数组下标从 0 开始(最常用),对于任意节点 i

父节点索引:parent(i) = (i - 1) / 2(整数除法,向下取整)
左子节点索引:left(i) = 2 * i + 1
右子节点索引:right(i) = 2 * i + 2

3. 堆的基本操作

3.1 插入(Insert)

把新元素插到数组末尾(保持完全二叉树形态)。

执行 上浮(sift up / heapify-up):不断和父节点比较,若不满足堆性质就交换。

时间复杂度:O(log n)。

3.2 删除堆顶(Extract)

取出根节点(最大或最小值)。

把最后一个元素移到根节点。

执行 下沉(sift down / heapify-down):不断和子节点比较并交换,直到满足堆性质。

时间复杂度:O(log n)。

3.3 建堆(Heapify)

自底向上调整:从最后一个非叶子节点开始,逐步下沉。

时间复杂度:O(n)。

4、堆的应用场景

堆的核心优势是 “快速获取极值”,主要应用包括:

  • 堆排序:利用堆的特性实现排序,时间复杂度 O (n log n),空间复杂度 O (1)(原地排序)。

  • 优先队列(Priority Queue):堆是优先队列的标准实现方式。优先队列的核心需求是 “每次取出优先级最高的元素”,堆的 “堆顶是极值” 特性完美匹配这一需求。

  • Top K 问题:从海量数据中快速找到前 K 个最大 / 最小元素(如 “前 10 名高分学生”)。

  • 中位数查找:用两个堆维护数据(大顶堆存左半部分数据,小顶堆存右半部分数据),堆顶分别为左半最大值和右半最小值,可 O (1) 获取中位数。

5、堆排序

如果把一个无序数组通过桶排序算法变成有序数组?

堆排序分为 构建堆排序(提取最大值并调整堆) 两大阶段,具体步骤如下:

1. 构建大顶堆

将无序数组转换为大顶堆,确保整个二叉树满足大顶堆的性质。

起点: 从最后一个非叶子节点开始(索引为 n/2 - 1,n 为数组长度),因为叶子节点本身就是合法的堆。

解释:最后一个节点的索引是 n-1 ,最后一个非叶子节点的索引是 (n-1-1)/2(n 是数组长度,即最后一个节点的父节点)。选择从这里开始,是因为叶子节点本身已满足 “大顶堆属性”,无需堆化;从后向前遍历非叶子节点,可确保每个节点的子树先满足堆属性,进而让整个数组成为大顶堆。

过程: 从后往前依次对每个非叶子节点执行 堆调整(heapify) 操作,确保以该节点为根的子树是大顶堆。

堆调整(heapify):对每个非叶子节点,执行 “向下堆化”:

  • 比较当前节点与其左、右子节点,找到三者中的最大值;
  • 若最大值不是当前节点,则交换当前节点与最大值节点(确保父节点≥子节点);
  • 交换后,需递归(或迭代)检查被交换的子节点,直到该节点及其子树满足大顶堆属性。

2. 排序(提取最大值并调整堆)

1) 提取堆顶最大值,放到已排序部分

  • 将 “堆顶(数组第一个元素,未排序部分的最大值)” 与 “当前堆的最后一个元素(未排序部分的最后一个元素)” 交换
  • 此时最大值被“移动到数组的末尾”,成为 “已排序部分的第一个元素”(随着循环,已排序部分会从后向前逐步扩大);
  • 原堆的最后一个元素被移到堆顶,破坏了堆的属性(新堆顶可能小于子节点)。

2)缩小堆的范围,恢复堆结构

  • 由于最大值已放入已排序部分,后续无需再处理它,因此 “堆的大小” 缩小 1(即忽略数组末尾的已排序元素);
  • 对新堆顶(原末尾元素)执行 堆调整(heapify),修复堆的属性,确保新堆的堆顶仍是 “当前未排序部分的最大值”。
#include <iostream>
#include <vector>
using namespace std;// 向下堆化(维护大顶堆)
// arr: 待堆化的数组
// n: 堆的大小
// i: 开始堆化的节点索引
void heapify(vector<int>& arr, int n, int i) {int largest = i;          // 初始化最大值为当前节点int left = 2 * i + 1;     // 左子节点索引int right = 2 * i + 2;    // 右子节点索引// 如果左子节点大于当前最大值,则更新最大值if (left < n && arr[left] > arr[largest]) {largest = left;}// 如果右子节点大于当前最大值,则更新最大值if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果最大值不是当前节点,则交换并递归堆化if (largest != i) {swap(arr[i], arr[largest]);heapify(arr, n, largest);}
}// 堆排序函数
void heapSort(vector<int>& arr) {int n = arr.size();// 1. 构建大顶堆// 从最后一个非叶子节点开始向上堆化for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 2. 提取最大值并维护堆// 每次将堆顶(最大值)与当前堆的最后一个元素交换// 然后减小堆的大小并重新堆化for (int i = n - 1; i > 0; i--) {swap(arr[0], arr[i]);  // 交换堆顶和当前堆的最后一个元素heapify(arr, i, 0);    // 对剩余的元素重新堆化}
}// 打印数组
void printArray(const vector<int>& arr) {for (int num : arr) {cout << num << " ";}cout << endl;
}int main() {vector<int> arr = {12, 11, 13, 5, 6, 7};cout << "排序前的数组: ";printArray(arr);heapSort(arr);cout << "排序后的数组: ";printArray(arr);return 0;
}
http://www.dtcms.com/a/443349.html

相关文章:

  • 河西区做网站的公司网站维护中要多久才能重新进入
  • 怎么让谷歌收录我的网站博客网站怎么建设
  • 网站收录查询邯郸网站优化平台
  • 数据库策略网站推广的有效方法有wordpress后台管理菜单改名
  • 蚌埠铁路建设监理公司网站做英语阅读的网站或是app
  • 纯静态网站seo网络营销推广的方式包括
  • 固镇网站建设哪家好?wordpress 自动关键词
  • 零食店网站构建策划报告正能量晚上看的网站2021
  • 郴州市建设局网站节能科怎样做diy家具网站
  • 找人做网站 网站定制开发合肥高端网站
  • 工程建设标准最新查询网站如何有效推广
  • 如何使用ftp上传网站深圳设计公司官网
  • 怎么做免费网站如何让百度收录wordpress pc客户端
  • cms网站模板下载杂志社网站建设意义
  • 商洛 网站建设做dapp开发广州
  • 德阳网站制作公司全国为何又突然做核酸了
  • 阜宁做网站的价格服务器建设网站软件
  • 美发店收银系统最新版本
  • 90设计网站会员全站通与电商模板的区别关于校园网站升级建设的报告
  • 网站怎么做支付宝付款小程序开发一个要多少钱
  • 总线锁(Bus Lock)是什么?
  • php网站建设与维护企业网站的设计公司
  • 网站开发7个基本流程图爬取1024上传到wordpress
  • 物联网网站开发公司手机如何网站模板
  • 产品展示网站设计网站建设报告心得体会
  • 模板建站能建个门户网站吗山东电力建设河北分公司网站
  • 企业档案网站建设华强北设计网站建设
  • 书画网站建设方案策划丰县做淘宝网站
  • 三北防护林体系建设网站网站开发补充合同范本
  • Linux发生信号send_signal函数以及配套工具函数的实现