堆排序就是利用堆的思想进行排序,总共分为两个步骤:
为了我只有由后往前向下调整就可以了,第一次要传的参数就是最后一个节点的父节点。然后依次传入前面是位置就可以了。
#include <stdio.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//建大堆
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] > a[child])
{
child = child + 1;
}
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
int main()
{
int arr[] = { 2,3,5,7,4,6,8,65,100,70,32,50,60 };//初始化一个小堆
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(arr, n, i);//通过向下调整建立大堆
}
for (int i = 0; i < n; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
//打印结果:
/*
100 70 60 65 32 50 8 3 7 4 2 5 6
*/
建堆和堆删除中都用到了向下调整,因此掌握了向下调整就可以完成堆排序。 提问:为什么升序需要建大堆呢? 回答:堆排序的本质是选择排序,每次都要选择一个最大的数到数组的最后一位,那么问题就变成了如何选择最大的数到数组最后一位,要找出堆中最大数就必须要用到大堆,因为大堆中最大的数就在堆堆顶,通过一次次的选择就可以将数组排序。 下面是画图解释:
#include <stdio.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//建大堆
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] > a[child])
{
child = child + 1;
}
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
int main()
{
int arr[] = { 2,3,5,7,4,6,8,65,100,70,32,50,60 };
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(arr, n, i);
}
//排序
for (int end = n - 1; end >= 0; --end)
{
swap(&arr[0], &arr[end]);
AdjustDown(arr, end, 0);
}
for (int i = 0; i < n; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
//打印结果:
/*
2 3 4 5 6 7 8 32 50 60 65 70 100
*/
O(N*logN)
TOP-K问题:即求数据结合中前k个最大元素或者最小元素,一般情况下数据量都比较大。 比如:专业前10名,时间500强、游戏中的前100的玩家。 对于TOP-K问题,能想到的最简单的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
下面我们将找出1000000个数中最大的10个数,为此我们需要建造一个大小为10的小堆。 主要步骤:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//建小堆
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] < a[child])
{
child = child + 1;
}
if (a[child] < a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
int main()
{
int n = 10000;
int* a = (int*)malloc(sizeof(int) * n);
srand((unsigned int)time(NULL));//生成随机种子
for (int i = 0; i < n; ++i)
{
a[i] = rand() % 1000000;//数据范围:[0,999999]
}
//选出数据中最大的10个数,建小堆
//为了证明确实可以找到,我们需要自己设置10个大于范围的数,如果最后被找到就说明正确
a[5] = 1000000 + 1;
a[55] = 1000000 + 2;
a[99] = 1000000 + 3;
a[1001] = 1000000 + 4;
a[123] = 1000000 + 5;
a[124] = 1000000 + 6;
a[18] = 1000000 + 7;
a[1111] = 1000000 + 8;
a[999] = 1000000 + 9;
a[100] = 1000000 + 10;
int heap[10] = { 0 };
for (int i = 0; i < n; ++i)
{
if (a[i] > heap[0])
{
heap[0] = a[i];
AdjustDown(heap, 10, 0);
}
}
for (int i = 0; i < 10; ++i)
{
printf("%d ", heap[i]);
}
return 0;
}
//打印结果:
/*
1000001 1000002 1000003 1000004 1000009 1000006 1000005 1000007 1000008 1000010
*/
最后的结果确实是我能自己修改的数,说明查找成功。 完