最大最小堆的原理及C++实现
参考资料
堆的原理
最大堆,每个节点都比其左右子节点大。且是完全二叉树。
从根节点到任意节点路径上结点序列的有序性。
抽象数据描述
创建
插入
删除
排序
- 将待排序序列构建成一个堆 H[0……n-1],根据(升序降序需求)选择大顶堆或小顶堆;
- 把堆首(最大值)和堆尾互换;
- 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
- 重复步骤 2,直到堆的尺寸为 1。
实现及测试
#pragma once
#include "aux_sort.h"
//完全二叉树,根节点序号为1,序号按层级从左往右增加
//一个节点为i,则其父节点序号为 i/2, 左子节点为 2*i, 右子节点为 2*i+1
// 9[1]
// / \
// 8[2] 7[3]
// / \ / \
// 6[4] 5[5] 4[6] 3[7]
class MaxHeap{
public:
MaxHeap(int maxSize);
void insert(int element);
int popMax();
vector<int> getSortedVec(); //堆排序
static MaxHeap* InitMaxHeap1(vector<int>& nums); //insert, O(nlogn)
static MaxHeap* InitMaxHeap2(vector<int>& nums); //adjust O(n)
static void _adjust(MaxHeap* heap, int index);
friend ostream& operator<<(ostream & os, MaxHeap& heap);
private:
vector<int> arr;
int size;
int capacity;
};
MaxHeap::MaxHeap(int maxSize)
:arr(maxSize+1)
{
size = 0;
capacity = maxSize;
arr[0] = INT_MAX; //为了方便跳出循环, 哨兵
}
void MaxHeap::insert(int element){
if(size==capacity){
cout<<"The heap is full."<<endl;
return;
}
int i = ++size;
for (; arr[i/2]<element; i/=2){
arr[i] = arr[i/2];
}
arr[i] = element;
}
int MaxHeap::popMax(){
//从最大堆中取出最大的元素(堆顶),删除。
//然后将堆尾的元素放到堆顶,然后判断其子节点是否比他大,大的话,就把该元素下移
if(size==0){
cout<<"The heap is empty."<<endl;
return INT_MIN;
}
int maxItem = arr[1]; //记录最大元素
int tmp = arr[size--]; //取出最尾元素,并 对 size减一
int parent, child;
for (parent=1; parent*2<=size/*存在左子节点*/; parent=child)
{
//让child 为左右子节点中较大的那个
child = parent*2; //先设为左子节点
if(child<size && arr[child]<arr[child+1]) //如果存在右子节点且右比左大
{child = child+1; /*设为右子节点*/ }
if(tmp<arr[child]){
arr[parent] = arr[child];
}
else{
break;
}
}
arr[parent] = tmp;
return maxItem;
}
MaxHeap* MaxHeap::InitMaxHeap1(vector<int>& nums){
int len = nums.size();
MaxHeap* heap = new MaxHeap(len);
for (int i=0; i<len; ++i){
heap->insert(nums[i]);
}
return heap;
}
MaxHeap* MaxHeap::InitMaxHeap2(vector<int>& nums){
int len = nums.size();
MaxHeap* heap = new MaxHeap(len);
heap->size = len;
for (int i=0; i<len; ++i){
heap->arr[i+1] = nums[i];
}
//从最后一个有子节点的节点开始,即 size/2, ~ 1
for (int i=heap->size/2; i>=1; --i){
_adjust(heap, i);
}
return heap;
}
void MaxHeap::_adjust(MaxHeap* pheap, int index){
MaxHeap& heap = (*pheap);
int leftChild = index*2;
int rightChild = leftChild+1;
int largest = index;
if (leftChild<=heap.size && heap.arr[leftChild]>heap.arr[largest])
largest = leftChild;
if (rightChild<=heap.size && heap.arr[rightChild]>heap.arr[largest])
largest = rightChild;
if (largest!=index)
{
//swap (index, largest)
int tmp = heap.arr[index];
heap.arr[index] = heap.arr[largest];
heap.arr[largest] = tmp;
_adjust(pheap, largest);
}
}
//平均时间复杂度 最好情况 最坏情况 空间复杂度 排序方式 稳定
// nlogn nlogn nlogn 1 in no
vector<int> MaxHeap::getSortedVec(){
//把堆首(最大值)和堆尾互换;
//把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
//重复以上步骤,直到堆的尺寸为 1。
vector<int> pre = arr;
int preSize = size;
for (int i=size; i>=1; --i)
{
int tmp = arr[1];
arr[1] = arr[size];
arr[size] = tmp;
--size;
_adjust(this, 1);
}
vector<int> result(++arr.begin(), arr.end()); //返回arr[1,size]
arr = pre;
size = preSize;
return result;
}
ostream& operator<<(ostream & out, MaxHeap& heap){
int levelEnd = 1;
int levelCount = 1;
for (int i=1; i<=heap.size; ++i){
out<<heap.arr[i]<<" ";
if(i==levelEnd){
out<<endl;
levelEnd += 2*levelCount;
levelCount *= 2;
}
}
return out;
}
void testHeap()
{
vector<int> arr;
for (int i = 0; i < 11; i++){
arr.push_back(rand()%20);
}
cout<<"Input arr: "<<arr<<endl;
MaxHeap* heap = MaxHeap::InitMaxHeap1(arr);
cout<<"The max heap initialized1 from arr:\n"<<*heap<<endl<<endl;
MaxHeap* heap2 = MaxHeap::InitMaxHeap2(arr);
cout<<"The max heap initialized2 from arr:\n"<<*heap2<<endl;
cout<<"Sorted arr from heap: \n"<<heap2->getSortedVec()<<endl;
heap2->popMax();
heap2->insert(24);
cout<<"After pop max and insert 24:\n"<<*heap2<<endl;
cout<<"sorted arr: \n"<<heap2->getSortedVec()<<endl;
}