算法复杂度稳定:堆排序的时间复杂度为O(nlogn),并且不受输入数据的影响,因此在各种情况下都能保持较稳定的性能。
不占用额外空间:堆排序是一种原地排序算法,不需要额外的空间来存储中间结果,因此空间复杂度为O(1)。
适用于大数据量:由于堆排序的时间复杂度较低且不占用额外空间,因此适用于大数据量的排序。
要知道在广大排序方法中,时间复杂度n*logn都算是非常精密的算法了,在保持高效的同时,不占用额外空间,通过反复进堆出堆节省一些无关紧要的空间内存,堆排序可谓将二者优势结合的淋漓尽致。假设一个场景:全国有千万所美食餐厅,倘若要选出TOP10家评分最高,你会怎么做呢?这里我推荐建堆排序。
如何尝试以上猜想呢?
通过随机数rand创建,以时间戳为种子,+i是为了让数据更加丰富。
#include
#include
void CreatData()
{
int n = 10000000;
srand(time(0));
const char* file = "file.txt";
FILE* fin =fopen(file, "w");
if (fin == NULL)
{
perror("fopen error");
}
for (int i = 0; i < n; i++)
{
int x = (rand()+i) % 10000000;
fprintf(fin,"%d\n", x);
}
fclose(fin);
}
因为最终需要检验是否数据真正是否为Top10,所以我们手动加入10个一定大于10000000的数据。
找出file.txt打开后手动加入10个值:10000001,10000002......10000010.
int* minheap = (int*)malloc(sizeof(int) * k);
if (minheap == NULL)
{
perror("malloc error");
return;
}
//将前十个数据放入堆内
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &minheap[i]);
Adjustup(minheap, i);
}
大的数自然沉淀下去,堆顶永远是10个数中最小的。
int x = 0;
while (fscanf(fout, "%d", &x) != EOF)
{
if (x > minheap[0])
{
minheap[0] = x;
AdjustDown(minheap, k, 0);
}
}
其中Adjustup和AdjustDown为向上调整算法和向下调整算法,我上篇文章提到过。代码我贴一下
Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
Adjustup(int* pheap, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (pheap[child] < pheap[parent])
{
Swap(&pheap[parent], &pheap[child]);
child = parent;
parent = (parent - 1) / 2;
}
else break;
}
}
void AdjustDown(int* pheap, int k, int parent)
{
int child = parent * 2 + 1;
while (child < k)
{
// 假设左孩子小,如果解设错了,更新一下
if (child + 1 < k && pheap[child + 1] < pheap[child])
{
++child;
}
if (pheap[child] < pheap[parent])
{
Swap(&pheap[child], &pheap[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
需要知道原理请看:调整算法
//打印堆元素
for (int i = 0; i < k; i++)
{
printf("%d ", minheap[i]);
}
开始运行,看看效果:
如图成功找到了,我们安插进去的10个“间谍”。
假设向下调整一次是一个时间复杂度,那么需要求出有多少次向下调整。
由于叶子节点无需向下调整,则仅需要从h-1行开始计算。
由高中数学知识不难求出:T(h)=2^(h-2)*1+2^(h-3)*2.......+2^1*(h-2)+2^0*(h-1)
求和得出:2^h-1-h
h是树高,N是节点数。满二叉树性质:2^h-1=N <=> h=log2(N+1)
所以向下:T(N)=N-log2(N+1) 时间复杂度略小于O(N)
向上调整建堆从叶子节点开始就得调整:
T(h)=2^1*1+2^2*2+......2^(h-2)*(h-2)+2^(h-1)*(h-1)
求和得T(h)=2^(h)*(h-1)-2^(h-1)+1
得T(N)=-N+(N+1)*(log2(N+1)-1)+1
所以向上:时间复杂度为N*logN
由于前方证明建大堆更有优势,并且减少了写向上调整的步骤
所以思路为:1 .建大堆
2. 将最大数换到堆末
3. 如此反复直到大堆只剩堆顶
void Heapsort(int* a, int n)
{
//建大堆
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdJustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdJustDown(a, end, 0);
--end;
}
}
虽然建堆时间复杂度为O(N)但在调整时是N*logN所以整体约等于N*logN
堆已建好
进行调整
int main()
{
int a[16] = { 11,23,12,15,78,99,0,23,26,21,2,4,65,78,3,5 };
Heapsort(a, 16);
for (int i = 0; i < 16; i++)
{
printf("%d ", a[i]);
}
}
不出意外成功,堆排序完成,大家可以自行尝试。感谢收看,请您三联支持一下哦!