堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆
堆的性质
- 堆中某个节点的值总是不大于或不小于其父节点的值
- 堆总是一棵完全二叉树
堆排算法分析
1、创建堆
例如,给出一个数组,在逻辑思上将它看作一个完全二叉树
int arr[] = {1, 5, 3, 8, 7, 6};
2、通过向下调整算法,将其调整为大根(小根)堆
此处调整为大根堆
向下调整算法过程:
- 找到最后一个非叶子结点k,判断k左右孩子是否存在,找到左右孩子中较大的值m
- 比较k与m的大小,k小于m,交换两者的值
- k不小于m则寻找下一个非叶子结点继续判断,直到判断到根节点
- 交换元素后可能破坏已经调整好的堆结构
- 因此,每次交换元素都需要递归重新构造堆结构
图解:
3、排序
排序过程:
- 交换根节点和最后一个结点
- 从根节点开始向下调整
- 交换根节点和倒数第二个结点
- 从根节点向下调整
- …
图解:
堆排实现(C++)
#include<iostream>
#include <vector>
using namespace std;
void AdDown(vector<int> &arr, int size, int index)
{
int parent = index;
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && arr[child + 1] > arr[child]) //如果右孩子存在,则找到左右孩子中较大者
child++;
if (arr[parent] < arr[child]) //此处实现大根堆,比较父子结点,将较大的结点向上迭代
{
swap(arr[parent], arr[child]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapSort(vector<int> &arr)
{
if (arr.empty())
return;
int n = arr.size();
for (int i = n / 2 - 1; i >= 0; --i) //叶子节点为n,其父节点为 (n-1)/2,最后一个节点为 n - 1,其父节点为 (n-1-1)/2 --> n/2-1
AdDown(arr, n, i);
for (int j = n - 1; j >= 0; --j)
{
swap(arr[0], arr[j]);
AdDown(arr, j, 0);
}
}
void Print(vector<int> arr)
{
for (auto e : arr)
cout << e << " ";
cout << endl;
}
void TestHeapSort()
{
vector<int> v{ 5, 4, 3, 6, 1, 3, 8, 7, 0, 5, 6 };
HeapSort(v);
Print(v);
}
int main()
{
TestHeapSort();
return 0;
}
本文完~