堆的定义
堆(Heap)是一种数据结构,堆通常是可以被看做一棵完全二叉树的数组对象。
- 堆的特性
- 结构性:用数组表示的完全二叉树
- 有序性:任一节点的关键字是其子树所有节点的最大值(或最小值)
堆的类型
最大堆(MaxHeap):也称“大顶堆”,有序性表示为根节点存储的值为子树所有节点的最大值
最小堆(MinHeap):也称“小顶堆”,有序性表示为根节点存储的值为子树所有节点的最小值
堆结构的创建
//定义堆结构
typedef struct HNode{
int* Data; //存储数据的数组
int Size; //当前堆中的元素个数
int capacity; //最大容量
HNode() //构造函数进行初始化
{
Data = new int[100];
Size = 0;
Data[0] = 1000;// 哨兵
capacity = 100;// 可存入100个数据
}
}HNode,*Heap;
大顶堆的操作
插入操作
- 在进行插入时,首先判断堆是否已满,堆满则不进行插入,堆未满,进行插入;
- 先将元素存入堆尾(Size + 1)的位置,再进行向上比较,如果大于其父节点,则向上替换,直到替换至根节点
//大顶堆插入操作
void Insert_MaxHeap(Heap H,int x)
{
if(H->Size == H->capacity) //检查堆是否已满
return;
++H->Size; // 插入元素,首先插入到堆尾
int position = H->Size;
for(;H->Data[position / 2] < x;position /= 2)
H->Data[position] = H->Data[position / 2];
H->Data[position] = x;
}
删除操作
- 在进行删除时,首先检查堆是否为空,为空,则不进行删除,不为空,执行删除操作
- 将堆尾元素保存至堆的第一个元素,从上而下,判断该元素是否大于其孩子节点的值,如果大于,则直接返回,小于,则进行替换,直到最后一个元素
//大顶堆删除堆顶元素
void Delete_MaxHeap(Heap H)
{
int tmp = H->Data[H->Size]; //先存储堆中最后一个元素
H->Size--;
int parent = 1;
int child = 0;
for(;parent * 2 <= H->Size;)
{
child = parent * 2;
if(child < H->Size && H->Data[child] < H->Data[child + 1]) //在孩子节点中寻找最大的
child++;
if(tmp > H->Data[child])
break;
else
H->Data[parent] = H->Data[child];
parent = child;
}
H->Data[parent] = tmp;
}
创建大顶堆
创建一个大顶堆有两种思路:
- 进行一组循环插入,把所有元素进行插入操作,完成创建,但是这种方式,效率较低,时间复杂度为O(n logn)
- 将要创建的数据无序插入堆中,然后对堆中的元素进行调整,从最后的一棵树开始,使得每棵树都是一个大顶堆
方法一
//创建大顶堆:方法一(O(N logN))
void Build_MaxHeap_1(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
Insert_MaxHeap(H,data[i]);
}
方法二
//创建大顶堆:方法二(O(N))
void Build_MaxHeap_2(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++) //无序存入堆中
H->Data[i + 1] = data[i];
H->Size = data.size();
int judge = H->Size / 2;
for(;judge > 0;judge--) //从最后一棵右孩子节点的树开始调整
{
int tmp = H->Data[judge];
int parent = judge;
int child = judge;
while(parent*2 <= H->Size) //进行调整
{
child = parent * 2;
if(child < H->Size && H->Data[child] < H->Data[child + 1])
child++;
if(tmp > H->Data[child])
break;
else
H->Data[parent] = H->Data[child];
parent = child;
}
H->Data[parent] = tmp;
}
}
取堆顶元素
//大顶堆取堆顶元素
int Top_MaxHeap(Heap H)
{
if(H->Size == 0) //判断堆是否为空
return -1;
return H->Data[1];
}
小顶堆的操作
插入操作
- 在进行插入时,首先判断堆是否已满,堆满则不进行插入,堆未满,进行插入;
- 先将元素存入堆尾(Size + 1)的位置,再进行向上比较,如果小于其父节点,则向上替换,直到替换至根节点
//小顶堆插入操作
void Insert_MinHeap(Heap H,int x)
{
if(H->Size == H->capacity) //检查堆是否已满
return;
H->Data[0] = -999; //哨兵
++H->Size;
int position = H->Size;
for(;H->Data[position / 2] > x;position /= 2)
H->Data[position] = H->Data[position/2];
H->Data[position] = x;
}
删除操作
- 在进行删除时,首先检查堆是否为空,为空,则不进行删除,不为空,执行删除操作
- 将堆尾元素保存至堆的第一个元素,从上而下,判断该元素是否小于其孩子节点的值,如果小于,则直接返回,大于于,则进行替换,直到最后一个元素
//小顶堆删除堆顶元素
void Delete_MinHeap(Heap H)
{
if(H->Size == 0) //检查堆是否为空
return;
int tmp = H->Data[H->Size];
H->Size--;
int parent = 1;
int child = 0;
while(parent*2 <= H->Size)
{
child = parent*2;
if(child < H->Size && H->Data[child] > H->Data[child + 1])
child++;
if(tmp > H->Data[child])
H->Data[parent] = H->Data[child];
else
break;
parent = child;
}
H->Data[parent] = tmp;
}
创建小顶堆
创建一个小顶堆有两种思路:
- 进行一组循环插入,把所有元素进行插入操作,完成创建,但是这种方式,效率较低,时间复杂度为O(n logn)
- 将要创建的数据无序插入堆中,然后对堆中的元素进行调整,从最后的一棵树开始,使得每棵树都是一个小顶堆
方法一
//创建小顶堆:方法一(O(N logN))
void Build_MinHeap_1(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
Insert_MinHeap(H,data[i]);
}
方法二
//创建小顶堆:方法二(O(N))
void Build_MinHeap_2(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
H->Data[i + 1] = data[i];
H->Size = data.size();
int judge = H->Size / 2;
for(;judge > 0;judge--)
{
int tmp = H->Data[judge];
int parent = judge;
int child = 0;
while(parent*2 <= H->Size)
{
child = parent*2;
if(child < H->Size && H->Data[child] > H->Data[child + 1])
child++;
if(tmp > H->Data[child])
H->Data[parent] = H->Data[child];
else
break;
parent = child;
}
H->Data[parent] = tmp;
}
}
取堆顶元素
//小顶堆取堆顶元素
int Top_MinHeap(Heap H)
{
if(H->Size == 0)
return -1;
return H->Data[1];
}
打印堆
//打印堆
void Print(Heap H)
{
for(int i = 1;i < H->Size + 1;i++)
cout << H->Data[i] << " ";
cout << endl;
}
堆排序
这里给出了4中排序方式:
方式一:将最大堆堆顶元素依次出堆,存入一个数组中,最后返回数组,这样实现了元素从大到小的排序,但是会额外消耗O(n)的空间
方式二:将最小堆堆顶元素依次出堆,存入一个数组中,最后返回数组,这样实现了元素从小到大的排序,但是会额外消耗O(n)的空间
方式三:将最大堆堆顶元素与堆尾元素进行替换,再调整最大堆,重复n次,即可实现升序排序
方式四:将最小堆堆顶元素与堆尾元素进行替换,再调整最大堆,重复n次,即可实现降序排序
方式一:
//堆排序1:从大到小,借助额外空间,利用大顶堆
vector<int> HeapSort_1(Heap H)
{
vector<int> Sort;
int Size = H->Size;
for(int i = 0;i < Size;i++)
{
Sort.push_back(Top_MaxHeap(H));
Delete_MaxHeap(H);
}
return Sort;
}
方式二:
//堆排序2:从小到大,借助额外空间,利用小顶堆
vector<int> HeapSort_2(Heap H)
{
vector<int> Sort;
int Size = H->Size;
for(int i = 0;i < Size;i++)
{
Sort.push_back(Top_MinHeap(H));
Delete_MinHeap(H);
}
return Sort;
}
方式三:
//堆排序3:从小到大,不借助额外空间,利用大顶堆
void HeapSort_3(Heap H)
{
int Rear = H->Size;
for(;Rear > 1;)
{
int tmp = H->Data[Rear]; //保存堆尾元素,与删除操作类似
H->Data[Rear] = H->Data[1];
Rear--;
int parent = 1;
int child = 0;
while(parent * 2 <= Rear)
{
child = parent * 2;
if(child < Rear && H->Data[child] < H->Data[child + 1])
child++;
if(tmp > H->Data[child])
break;
else
H->Data[parent] = H->Data[child];
parent = child;
}
H->Data[parent] = tmp;
}
}
方式四:
//堆排序4:从大到小,不借助额外空间,利用小顶堆
void HeapSort_4(Heap H)
{
int Rear = H->Size;
for(;Rear > 1;)
{
int tmp = H->Data[Rear];
H->Data[Rear] = H->Data[1];
Rear--;
int parent = 1;
int child = 0;
while(parent*2 <= Rear)
{
child = parent * 2;
if(child < Rear && H->Data[child] > H->Data[child + 1])
child++;
if(tmp > H->Data[child])
H->Data[parent] = H->Data[child];
else
break;
parent = child;
}
H->Data[parent] = tmp;
}
}
测试数据
生成给定长度的一组不重复的随机数
//测试数据
vector<int> randVector(int num) {
vector<int> result;
result.clear();
result.reserve(num);
srand((int)time(0));
for (int i = 0; i < num; i++)
{
result.push_back(i);
}
int p1;
int p2;
int temp;
while (--num)
{
p1 = num;
p2 = rand() % num;
temp = result[p1];
result[p1] = result[p2];
result[p2] = temp;
}
return result;
}
完整程序
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std;
//定义堆结构
typedef struct HNode{
int* Data;
int Size;
int capacity;
HNode()
{
Data = new int[100];
Size = 0;
Data[0] = 1000;// 哨兵
capacity = 1000;// 可存入1000个数据
}
}HNode,*Heap;
//大顶堆插入操作
void Insert_MaxHeap(Heap H,int x)
{
if(H->Size == H->capacity) //检查堆是否已满
return;
++H->Size; // 插入元素,首先插入到堆尾
int position = H->Size;
for(;H->Data[position / 2] < x;position /= 2)
H->Data[position] = H->Data[position / 2];
H->Data[position] = x;
}
//小顶堆插入操作
void Insert_MinHeap(Heap H,int x)
{
if(H->Size == H->capacity) //检查堆是否已满
return;
H->Data[0] = -999; //哨兵
++H->Size;
int position = H->Size;
for(;H->Data[position / 2] > x;position /= 2)
H->Data[position] = H->Data[position/2];
H->Data[position] = x;
}
//大顶堆取堆顶元素
int Top_MaxHeap(Heap H)
{
return H->Data[1];
}
//小顶堆取堆顶元素
int Top_MinHeap(Heap H)
{
if(H->Size == 0)
return -1;
return H->Data[1];
}
//大顶堆删除堆顶元素
void Delete_MaxHeap(Heap H)
{
int tmp = H->Data[H->Size]; //先存储堆中最后一个元素
H->Size--;
int parent = 1;
int child = 0;
for(;parent * 2 <= H->Size;)
{
child = parent * 2;
if(child < H->Size && H->Data[child] < H->Data[child + 1])
child++;
if(tmp > H->Data[child])
break;
else
H->Data[parent] = H->Data[child];
parent = child;
}
H->Data[parent] = tmp;
}
//小顶堆删除堆顶元素
void Delete_MinHeap(Heap H)
{
if(H->Size == 0) //检查堆是否为空
return;
int tmp = H->Data[H->Size];
H->Size--;
int parent = 1;
int child = 0;
while(parent*2 <= H->Size)
{
child = parent*2;
if(child < H->Size && H->Data[child] > H->Data[child + 1])
child++;
if(tmp > H->Data[child])
H->Data[parent] = H->Data[child];
else
break;
parent = child;
}
H->Data[parent] = tmp;
}
//创建大顶堆:方法一(O(N logN))
void Build_MaxHeap_1(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
Insert_MaxHeap(H,data[i]);
}
//创建小顶堆:方法一(O(N logN))
void Build_MinHeap_1(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
Insert_MinHeap(H,data[i]);
}
//创建大顶堆:方法二(O(N))
void Build_MaxHeap_2(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
H->Data[i + 1] = data[i];
H->Size = data.size();
int judge = H->Size / 2;
for(;judge > 0;judge--)
{
int tmp = H->Data[judge];
int parent = judge;
int child = judge;
while(parent*2 <= H->Size)
{
child = parent * 2;
if(child < H->Size && H->Data[child] < H->Data[child + 1])
child++;
if(tmp > H->Data[child])
break;
else
H->Data[parent] = H->Data[child];
parent = child;
}
H->Data[parent] = tmp;
}
}
//创建小顶堆:方法二(O(N))
void Build_MinHeap_2(Heap H,vector<int> data)
{
for(int i = 0;i < data.size();i++)
H->Data[i + 1] = data[i];
H->Size = data.size();
int judge = H->Size / 2;
for(;judge > 0;judge--)
{
int tmp = H->Data[judge];
int parent = judge;
int child = 0;
while(parent*2 <= H->Size)
{
child = parent*2;
if(child < H->Size && H->Data[child] > H->Data[child + 1])
child++;
if(tmp > H->Data[child])
H->Data[parent] = H->Data[child];
else
break;
parent = child;
}
H->Data[parent] = tmp;
}
}
//打印堆
void Print(Heap H)
{
for(int i = 1;i < H->Size + 1;i++)
cout << H->Data[i] << " ";
cout << endl;
}
//堆排序1:从大到小,借助额外空间,利用大顶堆
vector<int> HeapSort_1(Heap H)
{
vector<int> Sort;
int Size = H->Size;
for(int i = 0;i < Size;i++)
{
Sort.push_back(Top_MaxHeap(H));
Delete_MaxHeap(H);
}
return Sort;
}
//堆排序2:从小到大,借助额外空间,利用小顶堆
vector<int> HeapSort_2(Heap H)
{
vector<int> Sort;
int Size = H->Size;
for(int i = 0;i < Size;i++)
{
Sort.push_back(Top_MinHeap(H));
Delete_MinHeap(H);
}
return Sort;
}
//堆排序3:从小到大,不借助额外空间,利用大顶堆
void HeapSort_3(Heap H)
{
int Rear = H->Size;
for(;Rear > 1;)
{
int tmp = H->Data[Rear];
H->Data[Rear] = H->Data[1];
Rear--;
int parent = 1;
int child = 0;
while(parent * 2 <= Rear)
{
child = parent * 2;
if(child < Rear && H->Data[child] < H->Data[child + 1])
child++;
if(tmp > H->Data[child])
break;
else
H->Data[parent] = H->Data[child];
parent = child;
}
H->Data[parent] = tmp;
}
}
//堆排序4:从大到小,不借助额外空间,利用小顶堆
void HeapSort_4(Heap H)
{
int Rear = H->Size;
for(;Rear > 1;)
{
int tmp = H->Data[Rear];
H->Data[Rear] = H->Data[1];
Rear--;
int parent = 1;
int child = 0;
while(parent*2 <= Rear)
{
child = parent * 2;
if(child < Rear && H->Data[child] > H->Data[child + 1])
child++;
if(tmp > H->Data[child])
H->Data[parent] = H->Data[child];
else
break;
parent = child;
}
H->Data[parent] = tmp;
}
}
//打印排序数组
void Print_Vector(vector<int> num)
{
for(int i = 0;i < num.size();i++)
cout << num[i] << " ";
cout << endl;
}
//测试数据
vector<int> randVector(int num) {
vector<int> result;
result.clear();
result.reserve(num);
srand((int)time(0));
for (int i = 0; i < num; i++)
{
result.push_back(i);
}
int p1;
int p2;
int temp;
while (--num)
{
p1 = num;
p2 = rand() % num;
temp = result[p1];
result[p1] = result[p2];
result[p2] = temp;
}
return result;
}
int main()
{
vector<int> test = randVector(20);
vector<int> Sort1;
vector<int> Sort2;
//vector<>
Heap H1 = new HNode();
Heap H2 = new HNode();
Heap H3 = new HNode();
Heap H4 = new HNode();
cout << "----堆的相关操作----" << endl;
Build_MaxHeap_1(H1,test);
cout << endl << "方式一创建的大顶堆:" << endl;
Print(H1);
Build_MaxHeap_2(H2,test);
cout << endl << "方式二创建的大顶堆:" << endl;
Print(H2);
Build_MinHeap_1(H3,test);
cout << endl << "方式一创建的小顶堆:" << endl;
Print(H3);
Build_MinHeap_2(H4,test);
cout << endl << "方式二创建的小顶堆:" << endl;
Print(H4);
Sort1 = HeapSort_1(H1);
cout << endl << "堆排序1:从大到小,借助额外空间,利用大顶堆" << endl;
Print_Vector(Sort1);
Sort2 = HeapSort_2(H3);
cout << endl << "堆排序2:从小到大,借助额外空间,利用小顶堆" << endl;
Print_Vector(Sort2);
HeapSort_3(H2);
cout << endl << "堆排序3:从小到大,不借助额外空间,利用大顶堆" << endl;
Print(H2);
HeapSort_4(H4);
cout << endl << "堆排序4:从大到小,不借助额外空间,利用小顶堆" << endl;
Print(H4);
return 0;
}