文章目录
- 前言:
- 如何实现堆排
- 什么是堆排?
- 那我们如何自己实现一个堆排呢(以降序为例)?
- 思想及步骤(以 17,20,10,13,19,15为例)
- 注意:推荐大家使用向下建表法,因为向下建表法时间复杂度为O(n),向上建堆法时间复杂度为O(n*logn),想知道为什么下期博客揭晓。
前言:
相信看到这篇文章的柚柚们对于堆这个数据结构有啦一定的了解,至少清除堆是什么啦,当然可能有些柚柚们还不了解堆,可以去看博主之前写的关于堆(Heap)的博客喔链接在这里点击即可~
如何实现堆排
什么是堆排?
堆排:顾名思义是利用堆这种数据结构所设计的排序算法。
那我们如何自己实现一个堆排呢(以降序为例)?
- 我相信肯定会有柚柚们想到把数据都放在一个大堆里,然后每次取堆顶元素放到数组里面,再把堆顶元素删掉,进行向下调整,_size–,重复该操作,直到_size<=0。
- 代码如下:
void test()
{
Heap hp;
HeapInit(&hp);
//建立一个数组对数组进行排序
int arr[] = { 17,20,10,13,19,15 };
//将数组元素放进小堆
for (int i = 0; i < 6; i++)
{
HeapPush(&hp, arr[i]);
}
int i = 0;
//每次取堆顶元素依次放入数组中,再把堆顶元素删除
while (!HeapEmpty(&hp))
{
arr[i++] = HeapTop(&hp);
HeapPop(&hp);
}
//输出数组
for (int i = 0; i < 6; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
//销毁堆
HeapDestroy(&hp);
}
注意:用这种方法空间复杂度为O(n),因为建堆需要额外开辟空间,那我们如何把空间复杂度降为O(1)呢,也就是说我们如何在原数组的基础之上完成排序,其实我们只需要利用堆的插入和删除中的向上调节法或者向下调节法,就可以做到~
思想及步骤(以 17,20,10,13,19,15为例)
思想:我们想要在原数组的基础上建堆然后排序的话,就只能采用这种方法,在原数组的基础上建好小堆(大堆)后,重复进行删除堆顶的操作,直到堆为空,此时数组的顺序则为降序(升序)。
删除堆顶的代码:
void HeapPop(Heap* php)
{
assert(php && php->size);
Swap(&php->arr[0], &php->arr[php->size - 1]);
--php->size;
AdjustDown(php->arr, 0, php->size);
}
步骤:
1.我们就在原数组上利用向上调节法(也可以用向下调节法建堆)建大堆,看文字可能不太理解,那就看看几张图叭。
第一步:插入17,此时为根节点不需要调整,插入成功。
第二步:插入20,此时其有父结点,与父节点比较大小,比父节点大,不需要交换,插入成功。
第三步:插入10,有父节点,则跟父节点比大小,比父节点小,与父节点交换,此时孩子结点指向根节点,不需要进行其它操作,插入成功。
第四步到第六步我就不赘述啦,会堆的插入就会啦。这里举例子也只是让大家知道在原数组如何建堆,其实和建堆思想是一样哒。
2.建完堆啦我们就可以排序啦,排序的思想其实和这个我们刚开始的思想是一样哒,用一个变量end储存剩下的数据最后一个的下标,然后让堆顶数据与arr[end]进行交换,end–,再对堆顶元素进行向下调整,一直重复直到end<=0,这样我们就完成啦排序。看文字太枯燥,那就看看图片叭~
第一步:堆顶元素10与arr[end]交换,此时end为5,然后end–,end = 4,再对堆顶元素进行向下调整,调整后如第二个图所示。
第二步:堆顶元素13与arr[end]交换,此时end为4,然后end–,end = 3,再对堆顶元素进行向下调整,调整后如第二个图所示。
后面过程也差不多都是一直重复这个步骤,所以博主也不多赘述,直接上代码啦
代码如下:
void HeapSort(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
AdjustUp(arr, i);
}
//向下调整算法建堆
//for (int i = (n-1-1)/2; i >= 0; i--)
//{
// AdjustDown(arr, i , n);
//}
//循环将堆顶数据跟最后位置的数据进行交换
int end = n - 1;
while (end > 0)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, 0, end);
end--;
}
}