一个数组其实也可以看成一棵完全二叉树,这个完全二叉树就是(二叉)堆,如图:
设数组下标从1开始,那给定一个下标i,可以很容易求出父节点和左右儿子的节点下标:
伪代码
PARENT(i)
1 return i/2
LEFT(i)
1 return 2i
RIGHT(i)
1 return 2i+1
我们知道,最大堆的性质是除根结点以外的所有节点i都有A[PARENT(i)]>=A[i].
那我们如何维护堆的性质呢?
比如给定一个下标i和数组arr,让你维护i以下节点的性质。
对于节点i和它的左右孩子(如果存在),找出其中值最大的下标,与arr[i]交换(该下标不是i时),递归。时间复杂度O(logn).
void max_heapify(int i)
{
int l = 2 * i, r = 2 * i + 1,largest; //找到左右孩子
if (l <= heap_size && arr[l] > arr[i])largest = l;
else largest = i; //令largest为最大值的下标
if (r <= heap_size && arr[r] > arr[largest])largest = r;
if (largest != i)
{
swap(arr[i], arr[largest]); //当largest不是i时,需要维护
max_heapify(largest);
}
}
有了维护的操作,我们可以从下往上逐步维护来建立堆
每个叶子节点都可以看成是只有一个元素的堆
void build_heap(void)
{
for (int i = len/2; i >= 1; i--) //len/2+1至len均为叶子节点,不需要维护
{
max_heapify(i);
}
}
现在我们就可以进行堆排序了
思路是每次将根结点值与heap_size(这是有效节点的数量)下标对应的值交换,再让heap_size减一,即剥离原来heap_size的节点(这个节点在原来堆中最大,现在放在目前堆数组的末尾)
void heap_sort(void)
{
build_heap();
for (int i = len; i >= 2; i--) //只需到2,即进行len-1次剥离操作
{
swap(arr[1], arr[i]); //将目前最大的放后面
heap_size--;
max_heapify(1); //从根结点开始维护堆的性质
}
}
完整代码如下
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<string>
#include<map>
#include<utility>
using namespace std;
vector<int>arr;
int len;
int heap_size;
void max_heapify(int i)
{
int l = 2 * i, r = 2 * i + 1,largest;
if (l <= heap_size && arr[l] > arr[i])largest = l;
else largest = i;
if (r <= heap_size && arr[r] > arr[largest])largest = r;
if (largest != i)
{
swap(arr[i], arr[largest]);
max_heapify(largest);
}
}
void build_heap(void)
{
for (int i = len/2; i >= 1; i--)
{
max_heapify(i);
}
}
void heap_sort(void)
{
build_heap();
for (int i = len; i >= 2; i--)
{
swap(arr[1], arr[i]);
heap_size--;
max_heapify(1);
}
}
int main()
{
cin >> len;
heap_size = len;
arr.resize(len+1);
for (int i = 1; i <= len; i++)
{
cin >> arr[i];
}
heap_sort();
for (int i = 1; i <= len;i++)cout << arr[i] << " ";
return 0;
}
如觉得博文不错,关注点赞支持一下呗