堆排序:高效构建大顶堆实战

一、先解答上次的思考题

对树:10,7,15,3,9,12,18

  • 删除 15 后中序:3 7 9 10 12 18
  • 再删除 7 后中序:3 9 10 12 18

二、今天学习目标

  1. 什么是堆、大顶堆、小顶堆
  2. 堆的结构:用数组存完全二叉树
  3. 核心操作:上浮、下沉
  4. 堆实现优先队列(完整代码)

三、什么是堆(Heap)?

堆是一棵满足堆序性质的完全二叉树 ,通常用数组存储。

两种常用堆:

  • 大顶堆:父节点 ≥ 左右孩子,堆顶是最大值
  • 小顶堆:父节点 ≤ 左右孩子,堆顶是最小值

特点:

  • 结构是完全二叉树
  • 可以用数组高效存储
  • 取最值极快:O (1)
  • 插入 / 删除:O (log n)

四、数组存储完全二叉树(下标规律)

对于下标 i(从 0 开始):

  • 左孩子:2*i + 1
  • 右孩子:2*i + 2
  • 父节点:(i - 1) / 2

五、核心操作:下沉(向下调整)

以大顶堆为例:如果某个节点比孩子小,就和较大的孩子交换,一直往下沉,直到满足堆性质。

cpp 复制代码
// 大顶堆下沉调整
void siftDown(int arr[], int n, int i) {
    while (1) {
        int maxPos = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;

        if (left < n && arr[left] > arr[maxPos]) maxPos = left;
        if (right < n && arr[right] > arr[maxPos]) maxPos = right;

        if (maxPos == i) break;

        // 交换
        int temp = arr[i];
        arr[i] = arr[maxPos];
        arr[maxPos] = temp;

        i = maxPos;
    }
}

六、建堆 + 堆实现优先队列(取堆顶)

完整可运行代码

cpp 复制代码
#include <stdio.h>

// 下沉调整(大顶堆)
void siftDown(int arr[], int n, int i) {
    while (1) {
        int maxPos = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;

        if (left < n && arr[left] > arr[maxPos])
            maxPos = left;
        if (right < n && arr[right] > arr[maxPos])
            maxPos = right;

        if (maxPos == i)
            break;

        int temp = arr[i];
        arr[i] = arr[maxPos];
        arr[maxPos] = temp;

        i = maxPos;
    }
}

// 建堆
void buildHeap(int arr[], int n) {
    // 从最后一个非叶子节点往前建堆
    for (int i = n / 2 - 1; i >= 0; i--) {
        siftDown(arr, n, i);
    }
}

// 删除堆顶(取最大值)
int deleteMax(int arr[], int *n) {
    int maxVal = arr[0];
    // 最后一个元素放到堆顶
    arr[0] = arr[*n - 1];
    (*n)--;
    siftDown(arr, *n, 0);
    return maxVal;
}

// ==================== 测试 ====================
int main() {
    int arr[] = {3, 1, 7, 9, 4, 2, 8};
    int n = sizeof(arr) / sizeof(arr[0]);

    buildHeap(arr, n);

    printf("依次取出堆顶(大顶堆):");
    while (n > 0) {
        printf("%d ", deleteMax(arr, &n));
    }

    return 0;
}

七、运行结果

cpp 复制代码
依次取出堆顶(大顶堆):9 8 7 4 3 2 1

八、堆的典型用途

  • 优先队列:任务调度、定时器
  • 堆排序:O (n log n) 稳定高效
  • TopK 问题:找最大 / 最小的 k 个数

九、今日小练习

  1. 把数组 [5, 2, 8, 1, 3] 建成大顶堆
  2. 依次取出堆顶,观察输出是否降序
相关推荐
tangguofeng12 分钟前
基于分治策略的循环赛日程表问题的思路讲解
算法
hnjzsyjyj23 分钟前
洛谷 P1339:[USACO09OCT] Heat Wave G ← Bellman-Ford 算法
数据结构·bellman-ford 算法
起个名字费劲死了35 分钟前
VisionMaster集成深度学习算法(基础狗)
人工智能·深度学习·算法
lengxuenong38 分钟前
枚举例题分析
c++·算法·青少年编程
Navigator_Z1 小时前
LeetCode //C - 1269. Number of Ways to Stay in the Same Place After Some Steps
c语言·算法·leetcode
2601_965742221 小时前
短视频脚本创作方法分享:本地生活类账号的起步思路
大数据·人工智能·算法·ai·新媒体运营·生活
55873生态系统手记1 小时前
读完 55873 资产钱包的技术栈,我重新理解了 "合规钱包" 应该长什么样
算法·架构
旋生万物1 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
HRTOS1 小时前
HRTOS Shell继续完善:优化printf缓冲区与字符串越界保护,国庆后随新版本更新
运维·单片机·嵌入式硬件·算法·51单片机
邵奈一2 小时前
当沉香之乡遇上 Seed-2.1-pro:我用 AI 给村子做了一个数字门面
算法·架构