什么是堆?
堆(Heap)是一种特殊的完全二叉树结构,它通常被用作优先队列的实现,或者是在实现如堆排序这样的算法时用到。堆有两种主要的类型:最大堆(Max Heap)和最小堆(Min Heap)。
完全二叉树:除了最后一层外,每一层都被完全填满,并且所有节点都尽可能地向左对齐。也就是说,除最后一层外,所有层均为满层。最后一层,所有节点的排布都要靠左,直至排满。(个人理解)。
最大堆:每个父节点的值都大于其子节点的值。(所有父亲都比儿子大)根节点最大
最小堆:每个父节点的值都小于其子节点的值。(所有父亲都比儿子小)根节点最小
假装有图
C++中堆的实现
C++中,标准库并没有直接提供堆的数据结构。可以利用优先队列实现最大堆和自己手动创建堆。
使用std::priority_queue
在C++中,标准库并没有直接提供堆的数据结构,但是提供了std::priority_queue
,它底层默认使用最大堆实现。
#include <iostream>//用于输入输出操作
#include <queue>
#include <vector>
int main() {
// 创建一个最大堆(priority_queue 默认为最大堆)
std::priority_queue<int> maxHeap;
//采用std::greater<int>,可以将priority_queue 转换为最小堆
// std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
// 向堆中插入元素,插入后,堆会自动维护最大堆的性质
maxHeap.push(10);
maxHeap.push(5);
maxHeap.push(20);
maxHeap.push(15);
// 输出并删除堆顶元素(最大元素)
std::cout << "Heap elements in descending order:" << std::endl;
//通过 while 循环遍历并输出堆中的元素,堆顶元素最大,
//每次 pop 操作后堆顶会更新为下一个最大元素。
while (!maxHeap.empty()) {
std::cout << maxHeap.top() << " "; // 输出堆顶元素
maxHeap.pop(); // 删除堆顶元素
}
std::cout << std::endl;
return 0;
}
手动实现堆
#include <iostream>
#include <vector>
#include <algorithm> // For std::swap
class MaxHeap {
public:
MaxHeap() {}
// 插入元素
void insert(int value) {
heap.push_back(value);
heapifyUp(heap.size() - 1);
}
// 删除最大元素(堆顶)
void extractMax() {
if (heap.empty()) {
std::cout << "Heap is empty!" << std::endl;
return;
}
// 替换堆顶元素
heap[0] = heap.back();
heap.pop_back();
// 调整堆
heapifyDown(0);
}
// 打印堆内容
void printHeap() const {
for (int val : heap) {
std::cout << val << " ";
}
std::cout << std::endl;
}
private:
std::vector<int> heap;
// 向上堆化
void heapifyUp(int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (heap[index] > heap[parent]) {
std::swap(heap[index], heap[parent]);
index = parent;
} else {
break;
}
}
}
// 向下堆化
void heapifyDown(int index) {
int size = heap.size();
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) {
largest = left;
}
if (right < size && heap[right] > heap[largest]) {
largest = right;
}
if (largest != index) {
std::swap(heap[index], heap[largest]);
heapifyDown(largest);
}
}
};
C++中堆排序的实现
需要明确的是,堆本身是具有排序的。下面我们, 以最大堆为例子,其父节点的值大于等于其子节点的值。因此堆排序,只需要进行最大堆的重复建立,然后不断将堆顶元素(最大)与末尾元素交换并重新调整堆,使得数组逐渐有序,直到排序完成。(堆可以利用数组建立)。
- 堆排序的时间复杂度为O(nlogn),n为取出元素,logn为建立堆的时间,所以是nlongn。
- 堆排序是一种原地排序算法,不需要额外的存储空间,只需要交换值。
下面是具体实现步骤:
首先是实现值交换
viod swap(int&a,int&b){
int temp;
temp=a;
a =b;
b=temp;
}
之后是构建最大堆(这个上面已经讲过了)
// 辅助函数,用于下沉调整,保持最大堆的性质
void heapify(vector<int>& arr, int n, int i) {
int largest = i; // 初始化最大值为根
int left = 2 * i + 1; // 左子节点
int right = 2 * i + 2; // 右子节点
//这里有一点强调的是,数组构建的堆中,父节点为i,左节点为2i+1,右节点为2i+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]);
// 调用max heapify在减少的堆上
heapify(arr, i, 0);
}
}