堆结构和堆排序
堆结构和堆排序
1)基本思想:
1.堆结构:利用数组来模拟堆(以完全二叉树形式构造),用数组下标充当堆的节点位置,并用一个变量size表示堆的大小
2.堆排序:利用大根堆,通过将根节点的数和最后一个节点的数交换位置,可以将最大数排到最后面,然后再对堆进行调整,使其重新变成大根堆,通过不断操作,完成排序
2)代码实现:
#include<iostream>
using namespace std;
// 堆结构和堆排序 (以大根堆为例)
// 用数组来模拟堆,size代表堆的大小,数组下标可以代表根和左右孩子的位置
/*
0
1 2
3 4 5 6
*/
// 如果一个节点 为 i, 那么他的父节点就为 (i-1)/2(向下取整),左孩子就为 i*2+1,右孩子为i*2+2;
// 交换数组中两个元素的位置
void swap(int arr[],int idx1,int idx2){
int temp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = temp;
}
// i 位置的数 向上调整大根堆,令任意子树都满足 根节点 > 左右孩子
void heapInsert(int arr[],int i){
// 一直与父节点比较,当小于等于父节点或者没有父节点时退出
while(arr[i] > arr[(i-1)/2] && i != 0){
//如果比父节点大,就交换位置
swap(arr,i,(i-1)/2);
// 更新i的位置
i = (i-1)/2;
}
}
// i 位置的数 向下调整大根堆,当前堆的大小为size
void heapify(int arr[],int i,int size){
//左孩子位置
int l = i*2+1;
//判断是否有左孩子
while(l<size){
// 判断是否存在右孩子,如果存在,判断左右孩子哪个大,并记录较大的那个孩子的下标
int best = l+1 < size && arr[l+1] > arr[l] ? l+1 : l;
// 判断大孩子是否比父亲大,如果小于等于父亲,就令best = i;
best = arr[best] > arr[i] ? best : i;
// 孩子不够强,不用调整了,直接break
if(best == i){
break;
}
// 交换位置,并且更新i的位置
swap(arr,best,i);
i = best;
l = i*2+1;
}
}
// 堆排序 时间复杂度为 O(nlogn)
// 思路: 先构造大根堆,然后再让根节点与最后一个节点交换位置,这样最后一个数就是最大的
// 然后进行 size-- 并且向下调整(heapify函数),再次形成大根堆,再交换,直到所有数都排完
// 从顶到底建堆 时间复杂度为 O(nlogn)
void heapSort1(int arr[],int len){
for(int i = 0;i<len;i++){
heapInsert(arr,i);
}
int size = len;
while(size > 1){
// 将根结点和 最后一个数交换,
swap(arr,0,--size);
//向下调整,重新形成大根堆
heapify(arr,0,size);
}
}
// 从底到顶建堆 时间复杂度为 O(n)
void heapSort2(int arr[],int len){
for(int i = len - 1;i>=0;i--){
heapify(arr,i,len);
}
int size = len;
while(size > 1){
swap(arr,0,--size);
heapify(arr,0,size);
}
}
int main(){
int n;
cin >> n;
int arr[100];
for(int i = 0;i<n;i++){
cin >> arr[i];
}
heapSort2(arr,n);
for(int i = 0;i<n;i++){
cout << arr[i] << " ";
}
return 0;
}
3)总结:时间复杂度为O(nlogn),非常高效
4.15补充
1)STL库中构建堆的方法:
#include <iostream>
#include <vector>
#include <algorithm> // 堆算法所在的头文件
int main() {
std::vector<int> vec = {10, 20, 5, 60};
// 1. 构建大顶堆 (默认)
std::make_heap(vec.begin(), vec.end());
std::cout << "大顶堆的堆顶: " << vec.front() << std::endl; // 输出: 60
// 2. 构建小顶堆
std::make_heap(vec.begin(), vec.end(), std::greater<int>());
std::cout << "小顶堆的堆顶: " << vec.front() << std::endl; // 输出: 5
// 3. 添加元素
vec.push_back(100); // 先添加到末尾
std::push_heap(vec.begin(), vec.end(), std::greater<int>()); // 再调整堆
std::cout << "添加100后小顶堆的堆顶: " << vec.front() << std::endl; // 输出: 5
// 4. 删除元素
std::pop_heap(vec.begin(), vec.end(), std::greater<int>()); // 将堆顶移到末尾
vec.pop_back(); // 真正删除末尾元素
std::cout << "删除后小顶堆的堆顶: " << vec.front() << std::endl;
return 0;
}
浙公网安备 33010602011771号