堆排序
在上一章节里我们完成了堆的实现和基本操作:https://blog.csdn.net/qq_35423154/article/details/104598702
这里就顺带讲一讲堆排序
堆排序是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。通过堆来进行数据选择
时间复杂度: O (N*logN)
空间复杂度: O (1)
稳定性:不稳定
下面来讲一下具体思路
如利用下面这段数据
int arr[] = {46, 74, 53, 14, 26, 36, 86, 65, 27, 34};
首先将它写成堆的形式
然后利用向下调整算法把他调整为一个大根堆
void AdjustDown(int *arr, int size, int root)
{
int parent = root;
int child = parent * 2 + 1;
while(child < size)
{
if(child + 1 < size && arr[child] < arr[child + 1])
{
++child;
}
if(arr[child] > arr[parent])
{
int temp = arr[parent];
arr[parent] = arr[child];
arr[child] = temp;
}
else
break;
parent = child;
child = parent * 2 + 1;
}
}
这个算法在上一章就讲过了,在这里就不说了。
在这里有一个需要注意的地方,如果要排升序,就要建立大根堆,如果排降序,就要建立小根堆。
按照正常的思路, 排升序应该把小的放在前面,应该建立小根堆,这样的算法不是不行,但是效率太低,因为当我们排完一趟之后,堆的结构就会被完全打乱,我们就需要再次建立一个小根堆,这样的效率是极其低下的。但是我们如果用大根堆,堆顶是最大的数据,我们把他和末尾数字交换,我们就把最大的数据放到最后了,按照这种方法一次将次大数据放到末尾,就完成了排序
这就是堆排序的核心,也就是选择排序的思路,每趟选择一个最大或者最小的元素放到它应该呆的位置上。
同时,使用大根堆还有一个优势,就是交换后堆的结构没有被破坏,只需要一趟向下排序算法就可以再次建立大根堆,这样的效率是非常高的。
void HeapSort(int *arr, int size)
{
int i = 0;
for(i = (size - 2) / 2 ; i >= 0; i--)
{
AdjustDown(arr, size, i);
}
for(i = size - 1; i > 0; i--)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
AdjustDown(arr, i, 0);
}
}
算法非常简短,首先建立一个大根堆,然后每趟排序堆顶和堆尾进行交换,再用一个向下排序算法恢复大根堆即可实现。
具体步骤
完整代码
void AdjustDown(int *arr, int size, int root)
{
int parent = root;
int child = parent * 2 + 1;
while(child < size)
{
if(child + 1 < size && arr[child] < arr[child + 1])
{
++child;
}
if(arr[child] > arr[parent])
{
int temp = arr[parent];
arr[parent] = arr[child];
arr[child] = temp;
}
else
break;
parent = child;
child = parent * 2 + 1;
}
}
void HeapSort(int *arr, int size)
{
int i = 0;
for(i = (size - 2) / 2 ; i >= 0; i--)
{
AdjustDown(arr, size, i);
}
for(i = size - 1; i > 0; i--)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
AdjustDown(arr, i, 0);
}
}