堆排序的问题
堆排序肯定要优于直接选择排序【O(N^2)】才会有价值。
那么排升序是用大堆还是小堆? 答案是建大堆。
升序,为什么不能建小堆?
问题在于选出最小的数字后如何选出次小的数字
建堆选出最小的数,花了O(N),紧接着选择次小的数,剩下的N-1个数继续建堆,又是O(N),
最终成为N^2{因为剩下数字的父子关系完全乱了,只能重新建堆,效率太低}
这样建堆排序的时间复杂度就和直接排序一样没有什么实际的意义。
不是不可以,而是没有什么价值
而通过建大堆,
首先选出最大的数字,让它与最后一个数字交换,紧接着选择次大的数,不把最后一个数看做是堆里面的,向下调整就能选出次大的
思路如图:
同理排降序 就应该建小堆,原因及思路和上面完全一致。
排升序代码如下:
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(NlogN)
[ N+NlogN,N相对于logN太小了可以忽略]
简单分析下,堆排序和冒泡排序的差别有多大
数字小了看不出来啥区别
假设排100W个数字
N^2 需要100亿次
NlogN 需要 100W*20 次 堆排的优势就显示出来了
完整代码:
#include <stdio.h>
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = 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;
}
if (a[child] > a[parent])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else //b情况
{
break;
}
}
}
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--;
}
}
int main()
{
//前提左右字树是小堆
int a[] = { 15,18,28,34,65,19,49,25,37,27 };
int n = sizeof(a) / sizeof(a[0]);
HeapSort(a, n);
return 0;
}