最小堆是指在树中,存在一个结点而且该结点有儿子结点,该结点的data域值都小于其儿子结点的data域值,并且它是一个完全二叉树(不是满二叉树)。
这里使用静态数组存储完全二叉树,对于完全二叉树采用顺序存储表示,那么对于任意一个下标为i(1 ≤ i ≤ n)的结点:
class MinHeap
{
public:
MinHeap(int size);
~MinHeap();
void Insert(int number);//最小堆插入元素
void Delete();//最小堆删除元素
void swap(int& a, int& b) { int temp = a; a = b; b = temp; }//交换元素
int GetNum() { return count - 1; }//获取最小堆元素个数
void printHeap();//打印堆元素
friend void sort(MinHeap& minheap,int* res,int n);//最小堆排序,res是排序后的数组指针
private:
int* p;//数组指针
int count;//最小堆元素数量
int maxsize;//最小堆的最大元素数量
};
最小堆的插入操作可以看成是“结点上浮”。当我们在向最小堆中插入一个结点我们必须满足完全二叉树的标准,那么被插入结点的位置的是固定的。而且要满足父结点关键字值不小于子结点关键字值,那么我们就需要去移动父结点和子结点的相互位置关系。
//最小堆插入元素
void MinHeap::Insert(int number)
{
if (count > maxsize) {
cout << "内存不足,请重新分配空间" << endl;
return;
}
//插入元素
p[count] = number;
//获取当前下标
int curCount = count;
//节点上浮
while (curCount/2)
{
//如果子节点比父节点小,交换节点值,即上浮
if (p[curCount] < p[curCount / 2])
swap(p[curCount], p[curCount / 2]);
//更新当前结点坐标值
curCount /= 2;
}
count++;
}
最大堆的删除操作,即从堆的根结点删除元素,同时在将最小堆的最后一个元素移动到根节点处,进行“下降”操作,使其重新平衡成为最小堆。
每次下降操作父节点都需要分对左子节点与右子节点进行判断
//最小堆删除元素
void MinHeap::Delete()
{
int curCount = 1;
int num = GetNum();//获取最小堆元素总数
p[curCount] = p[num];//删除根结点元素,并将其替换为尾部元素
p[num] = 0;
//下降操作
//当子节点存在时
while (2 * curCount <= num - 1)
{
//如果左子节点存在且父节点大于左子节点
if (p[curCount] > p[2 * curCount])
swap(p[curCount], p[2 * curCount]);
//如果右子节点存在且父节点大于右子节点
if(2 * curCount + 1 <= num - 1 && p[curCount] > p[2 * curCount+1])
swap(p[curCount], p[2 * curCount + 1]);
//更新当前结点坐标值
curCount *= 2;
}
count--;
}
原理和上面的删除是一样的,你可以认为进行了n次删除就能的到排序结果,不过在删除的根结点的时,把每次得到的根结点值插入到了另外的数组中了,最后返回这个另外的数组
//最小堆排序
void sort(MinHeap& minheap,int res[],int n)
{
//res = new int[minheap.count - 1];
int index = minheap.count;
for (int i = 0; i < n; i++)
{
int curCount = 1;
int num = index - 1;//获取最小堆元素总数
res[i] = minheap.p[curCount];
minheap.p[curCount] = minheap.p[num];//删除根结点元素,并将其替换为尾部元素
//下降操作,当子节点存在时
while (2 * curCount <= num - 1)
{
//如果左子节点存在且父节点大于左子节点
if (minheap.p[curCount] > minheap.p[2 * curCount])
swap(minheap.p[curCount], minheap.p[2 * curCount]);
//如果右子节点存在且父节点大于右子节点
if (2 * curCount + 1 <= num - 1 && minheap.p[curCount] > minheap.p[2 * curCount + 1])
swap(minheap.p[curCount], minheap.p[2 * curCount + 1]);
//更新当前结点坐标值
curCount *= 2;
}
index--;
}
}
#pragma once
#ifndef MINHEAP_H
#define MINHEAP_H
#include
using std::cout;
using std::cin;
using std::endl;
class MinHeap
{
public:
MinHeap(int size);
~MinHeap();
void Insert(int number);//最小堆插入元素
void Delete();//最小堆删除元素
void swap(int& a, int& b) { int temp = a; a = b; b = temp; }//交换元素
int GetNum() { return count - 1; }//获取最小堆元素个数
void printHeap();//打印堆元素
friend void sort(MinHeap& minheap,int* res,int n);//最小堆排序,res是排序后的数组指针
private:
int* p;//数组指针
int count;//最小堆元素数量
int maxsize;//最小堆的最大元素数量
};
MinHeap::MinHeap(int size)
{
maxsize = size;
p = new int[maxsize + 1];
if (p == nullptr) { cout << "内存分配失败!" << endl; exit(-1); }
count = 1;
}
MinHeap::~MinHeap()
{
if (p != nullptr) delete[] p;
}
//最小堆插入元素
void MinHeap::Insert(int number)
{
if (count > maxsize) {
cout << "内存不足,请重新分配空间" << endl;
return;
}
//插入元素
p[count] = number;
//获取当前下标
int curCount = count;
//节点上浮
while (curCount/2)
{
//如果子节点比父节点小,交换节点值,即上浮
if (p[curCount] < p[curCount / 2])
swap(p[curCount], p[curCount / 2]);
//更新当前结点坐标值
curCount /= 2;
}
count++;
}
//最小堆删除元素
void MinHeap::Delete()
{
int curCount = 1;
int num = GetNum();//获取最小堆元素总数
p[curCount] = p[num];//删除根结点元素,并将其替换为尾部元素
p[num] = 0;
//下降操作
//当子节点存在时
while (2 * curCount <= num - 1)
{
//如果左子节点存在且父节点大于左子节点
if (p[curCount] > p[2 * curCount])
swap(p[curCount], p[2 * curCount]);
//如果右子节点存在且父节点大于右子节点
if(2 * curCount + 1 <= num - 1 && p[curCount] > p[2 * curCount+1])
swap(p[curCount], p[2 * curCount + 1]);
//更新当前结点坐标值
curCount *= 2;
}
count--;
}
void MinHeap::printHeap()
{
cout << "开始打印:";
for (int i = 1; i < count; ++i)
cout << p[i] << " ";
cout << endl;
}
//最小堆排序
void sort(MinHeap& minheap,int res[],int n)
{
//res = new int[minheap.count - 1];
int index = minheap.count;
for (int i = 0; i < n; i++)
{
int curCount = 1;
int num = index - 1;//获取最小堆元素总数
res[i] = minheap.p[curCount];
minheap.p[curCount] = minheap.p[num];//删除根结点元素,并将其替换为尾部元素
//下降操作,当子节点存在时
while (2 * curCount <= num - 1)
{
//如果左子节点存在且父节点大于左子节点
if (minheap.p[curCount] > minheap.p[2 * curCount])
swap(minheap.p[curCount], minheap.p[2 * curCount]);
//如果右子节点存在且父节点大于右子节点
if (2 * curCount + 1 <= num - 1 && minheap.p[curCount] > minheap.p[2 * curCount + 1])
swap(minheap.p[curCount], minheap.p[2 * curCount + 1]);
//更新当前结点坐标值
curCount *= 2;
}
index--;
}
}
#endif // !MINHEAP_H
// MinHeap.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
using namespace std;
#include"minheap.h"
int main()
{
MinHeap myheap(256);
int sample[] = { 12,11,23,2,4,5,3,8,9,10 };
for (int i = 0; i < sizeof(sample)/sizeof(int); i++)
myheap.Insert(sample[i]);
cout << "最小堆元素总数:"<