? ? ? ? 堆排序是借用数据结构堆来进行排序的一种算法,所以要想弄明白堆排序,首先要弄明白堆。
? ? ? ? 首先我们先回顾一下堆:
? ? ? ? 大堆:头大尾小,父结点 >= 子结点
? ? ? ? 小堆:头小尾大,父结点 <= 子结点
? ? ? ? 堆排序就是借用大堆和小堆来进行排序。
? ? ? ? 那么问题来了?升序是用小堆吗?NO!NO!NO!这里许多人都会误以为升序小堆,降序大堆,现实是:升序大堆,降序小堆。
? ? ? ? 我们知道,堆顶元素是堆里面MAX或者MIN元素,但是其它位置的元素,我们并不知道它在整个堆中的大小。我们仅仅知道堆顶元素的大小。
? ? ? ? 以升序大堆为例:大堆堆顶元素是最大的一个,那么我们将堆顶元素与最后一个元素交换,然后将除了最后一个元素之外的其他堆中元素进行调整,继续成为大堆,此时堆顶元素为次大值;再将堆顶元素与倒数第二个元素交换。反复如此,最终数组的末尾到开头就是最大,次大,次次大,.....,最小。这就成了升序。
? ? ? ? 因此:从中我们得到了堆排最重要的几个东西:建堆,交换,调整。
? ? ? ? 下面以升序大堆为例:
? ? ? ? 建堆在前面树那里已经讲过,这里不多讲解。这里我们采用向下调整法建堆。直接上代码:
void AdjustDown_BigHeap(int* arr, int n, int parent)
{
int child = 2 * parent + 1;
while (child < n)
{
if (arr[child] < arr[child + 1] && child + 1 < n)
child = child + 1;
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = 2 * child + 1;
}
else
break;
}
}
? ? ? ? 我们从最后一个非叶子的结点开始向下调整,将最小子堆建成大堆;再逐渐向上走,将次小子堆建成大堆.........直到走到堆顶元素,向下调整,最终就建成一个大堆。
? ? ? ? 思想:先子堆,后整堆。
? ? ? ? 完成建堆后,将堆顶元素与末尾元素交换。
? ? ? ? 再继续向下调整,再交换,直到堆顶变成最小元素。
void HeapSort_incline(int* arr, int n)
{
//将数组建成大堆
//最后一个叶结点的父结点就是最后一个非叶子的结点
for (int i = ((n - 1) - 1) / 2; i >= 0; i--)
{
AdjustDown_BigHeap(arr, n, i);
}
//建成之后,交换,调整
for (int i = n - 1; i > 0; i--)
{
//交换堆顶元素和倒数第一,第二....的元素
Swap(&arr[0], &arr[i]);
//调整
//每次末尾交换过去的元素不再参与调整,恰好每次需要调整的就是i个
AdjustDown_BigHeap(arr, i, 0);
}
}
int arr[] = { 3,4,8,10,1,7,1,5,2,8,2,-1,-3 };
int n = sizeof(arr) / sizeof(int);
printf("原数组: ");
PrintArray(arr, n);
printf("\n");
printf("排序后: ");
HeapSort_incline(arr,n);
PrintArray(arr, n);
????????