堆排序:也是选择排序的一种,只不过是利用树形结构来进行排序
优点:时间复杂度为O(nlogn)
分为两种:
一:从小到大(非递减排序)
二:从大到小(非递增排序)
对于从小到大采用大顶堆来进行排序。
每次取大顶堆的堆顶,将堆顶与最后一个元素进行交换,然后在对堆进行排序调整使其变为大顶堆,然后再取堆顶元素,让堆顶元素再和倒数第二个元素进行交换,一直这样循环,知道只剩下一个元素没有进行交换,那么该组元素,就变成了一个有序的序列。
现将其变成大顶堆,如图:
然后再将堆顶元素,与最后一个元素进行交换,对这个堆再进行调整,不再管最后一个元素,然后再在交换在调整,直到只剩下一个堆顶元素的时候。也就排序完成了。
如图:
然后不再管78这个位置了,这已经是最大值了。
将前面的元素在进行一个调整为大顶堆即可。
如图:
再交换:再调整:
如图:
再交换,再调整
如图:
再交换,再调整
如图:
再交换,不再调整
如图:
因为交换完了之后,就只剩下堆顶一个元素了,没有元素再可供调整了。所以结束。此时数据元素变为了非递减的顺序。
接下来看代码该如何实现:
//向下调整堆(调为大堆)
void ADJustDown(HPDataType* a, int size, int parent)
{
assert(a);
//左孩子结点
int child = parent * 2 + 1;
//左孩子结点的下标小于数组的最大下标时,堆是一个完全二叉树
while (child < size)
{
//取出最大的孩子结点,只有当右孩子存在时,才能求两个中较大的一个孩子
if (a[child + 1] > a[child] && ((child + 1) < size))
{
child++;
}
//交换结点,向下调整
if (a[parent] < a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
//如果要调整的的这个结点,比起的孩子结点大的话,那就不用调整了
else
{
break;
}
}
}
//思想:开始将其变为一个大顶堆,然后每次将堆顶与最后一个元素进行交换,在令该队减少一个元素再次进行
void HeapSortSmallToBig(HPDataType* a, int size)
{
assert(a);
//size代表数组大小
//因为size在进行建堆调整的时候会发生变化,所以要记录下来以便输出
int start_size = size;
//给进来一个数组,现将该数组进行建堆
for (int i = (size - 2) / 2; i >= 0; i--)
{
ADJustDown(a, size, i);
}
//当size变为0的时候,那么该堆已经变为了一个有序的序列了
while (size > 1)
{
Swap(&a[0], &a[size - 1]);
size--;
//对于新的堆进行调整,时间复杂度O(logN)
ADJustDown(a, size, 0);
}
//该函数排序函数的时间复杂度为O(NlogN)
//调整完之后size变为了0,对于这个堆,size还应该是原来的size
size = start_size;
}
这就是采用堆排序,将数组元素进行非递减排序。
对于从大到小采用小顶堆来进行排序。
接下来看一下非递增排序。其实非递增排序和非递减排序思想差不多。
非递增排序就是,先将数组变成一个小顶堆,然后也是将堆顶元素与最后一个元素一次交换,在进行小顶堆的调整。直到最后在只剩下一个堆顶元素,那么数组也就排序完成了。
如图:
建成小顶堆:
交换第一次:
调整第一次;
再交换,再调整:
在交换,再调整:
再交换,再调整:
再交换:
因为只剩最后一个结点了,所以不在需要进行调整了,数组已经变成了一个非递增的序列了。
代码:
//小顶堆从上到下,调整
void ADJustDownSmallHeap(HPDataType* a, int size, int parent)
{
assert(a);
//左孩子结点
int child = parent * 2 + 1;
//左孩子结点的下标小于数组的最大下标时,堆是一个完全二叉树
while (child < size)
{
//取出最大的孩子结点,只有当右孩子存在时,才能求两个中较小的一个孩子
if (a[child + 1] < a[child] && ((child + 1) < size))
{
child++;
}
//交换结点,向下调整
if (a[parent] > a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
//如果要调整的的这个结点,比起的孩子结点大的话,那就不用调整了
else
{
break;
}
}
}
//对数组进行排序(从大到小) 采用小顶堆
void HeapSortBigToSmall(HPDataType* a, int size)
{
assert(a);
int start_size = size;
//先将该数组变为一个小顶堆
for (int i = (size - 2) / 2; i >= 0; i--)
{
ADJustDownSmallHeap(a, size, i);
}
while (size > 1)
{
Swap(&a[0], &a[size - 1]);
size--;
//每次交换之后就要向下调整
ADJustDownSmallHeap(a, size, 0);
}
}
这就是堆排序了。
最后再介绍一下对排序的优点:关键就是效率高。
适合大数据的排序。
比如有100亿的数据,让你进行排序的话,选择排序的时间复杂度为O(n²),那么次数为10亿*10亿
而堆排序的时间复杂度为O(nlogn),则其次数为10亿*30。这就整整减少了可以说是将近70亿的次数,这是多么的效率高。
因为排序的时候的时候要用到存储空间这将是节省了多么大的空间。
所以对于堆排序要好好的学习,并且加以利用。
最后正好今天四六级成绩出来了!小凯祝大家四六级都过!不用再转发杨超越,相信自己。
每天都要好好的学习英语哦!