欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > c++ 堆和堆排序

c++ 堆和堆排序

2025/2/25 1:13:33 来源:https://blog.csdn.net/www_dong/article/details/143195594  浏览:    关键词:c++ 堆和堆排序

什么是堆(Heap)?

堆(Heap)是一种基于完全二叉树的数据结构,用于快速查找最大值或最小值。它满足以下性质:

  • 完全二叉树:每一层从左到右填满,除了最后一层可以不完全填满,但节点必须靠左排列
  • 堆序性质:堆分为两种常见类型:
    • 大顶堆(Max-Heap):每个节点的值 ≥ 其子节点的值。根节点是最大值。
    • 小顶堆(Min-Heap):每个节点的值 ≤ 其子节点的值。根节点是最小值。

示例

  • 大顶堆
      10/  \5    3/ \2   1
  • 小顶堆
      1/ \3   2/ \5   4

堆的存储方式

数组存储:堆通常用数组表示,因为它是完全二叉树,利用数组的结构可以高效定位父节点和子节点。

  • 父节点索引parent(i) = (i - 1) / 2
  • 左子节点索引left(i) = 2 * i + 1
  • 右子节点索引right(i) = 2 * i + 2

数组表示例:

大顶堆 [10, 5, 3, 2, 1] 对应的结构为:

        10/  \5    3/ \2   1

堆的基本操作

插入元素

  • 将新元素插入到数组末尾,然后向上调整堆(上滤),确保堆序性质。

算法:

  1. 将元素插入数组末尾。
  2. 通过“上滤”操作,将其逐层与父节点比较,必要时交换。

时间复杂度:O(log n)

删除堆顶元素

  • 删除堆顶元素后,将最后一个元素移到堆顶,然后向下调整(下滤)。

算法:

  1. 删除堆顶元素,将最后一个元素放到堆顶。
  2. 通过“下滤”操作,将其逐层与子节点比较,必要时交换。

时间复杂度:O(log n)

构建堆

  • 从无序数组构建堆,可以通过heapify(堆化)操作实现。
    从最后一个非叶子节点开始,逐步向上调整。

时间复杂度:O(n)

堆实现(c++)

#include <iostream>
#include <vector>
using namespace std;// 调整为大顶堆
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 buildHeap(vector<int>& arr) {int n = arr.size();for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}
}// 打印数组
void printArray(const vector<int>& arr) {for (int i : arr) {cout << i << " ";}cout << endl;
}int main() {vector<int> arr = {4, 10, 3, 5, 1};cout << "原数组: ";printArray(arr);buildHeap(arr);cout << "构建大顶堆: ";printArray(arr);return 0;
}

堆排序(Heap Sort)

堆排序是基于大顶堆的排序算法。其过程如下:

  1. 将数组构建为大顶堆
  2. 堆顶元素(最大值)与末尾元素交换,并缩小堆的范围。
  3. 对剩余部分重新进行堆调整,直到所有元素有序。

堆排序代码:

#include <iostream>
#include <vector>
using namespace std;// 调整为大顶堆
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();// 构建大顶堆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);    // 调整剩余部分为大顶堆}
}// 打印数组
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;
}

代码分析

  • heapify 函数
    这是一个递归函数,用于调整部分数组为大顶堆。在构建和排序过程中都需要用它来维护堆的性质。
  • heapSort 函数
    • 第一步:从最后一个非叶子节点开始,依次构建大顶堆。
    • 第二步:逐次将堆顶(最大值)与末尾元素交换,并对剩余部分进行堆调整。
  • printArray 函数
    打印数组内容,帮助查看排序结果。

时间复杂度分析

  • 构建堆:O(n)
  • 调整堆:O(log n)(每次交换后调整堆)
  • 堆排序整体时间复杂度:O(n log n)

空间复杂度

  • 原地排序:不需要额外空间,空间复杂度为 O(1)

堆排序总结

  • 堆排序是一种不稳定的排序算法,因为在排序过程中相同元素的顺序可能会改变。
  • 适合用于需要原地排序且不要求稳定性的场景。
  • 堆排序的时间复杂度为 O(n log n),在大数据排序中表现稳定。

堆的应用

  • 优先队列:使用堆实现,可以快速获取最大值/最小值。
  • 堆排序:利用大顶堆完成排序,时间复杂度 O(n log n)。
  • 求 Top K 元素:利用小顶堆,可以在 O(n log k) 时间内找到数组的前 K 个最大元素。
  • 合并多个有序数组:使用小顶堆合并多个有序数组,时间复杂度 O(n log k)。

时间复杂度分析

操作时间复杂度
插入元素O(log n)
删除堆顶元素O(log n)
构建堆O(n)
堆排序O(n log n)

总结

  • 是一种高效的二叉树数据结构,适用于需要快速获取最大/最小值的场景。
  • 堆排序是一种原地排序算法,空间复杂度 O(1),但它是不稳定排序
  • 应用场景广泛,如优先队列、Top K 问题多路归并等。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词