堆排序
本篇博客假定读者已经掌握了完全二叉树和二叉堆的概念。
1.概念
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
误区
- 当我们学完二叉堆的时候,掌握了建立最大堆和最小堆的思想,也掌握了删除堆的操作,我们这个时候就在想完全可以利用插入删除操作也可以实现对数组的排序。
- 首先我们将数组建立成一个最大或最小堆,再进行删除操作将值返回给数组也可以进行排序。
- 分析一下该算法的时间复杂度。首先建立一个最大或最小堆需要花费O(N)的时间,然后进行删除操作需要O(logN)的时间,我们要执行N次删除操作,所以整体就需要O(NlogN)的时间。
问题: 该算法的主要问题在于它使用了一个附加的数组。因此该算法的主要问题在于它使用了一个附加的数组。因此存储需求增加一倍。注意,将第二个数组拷贝回第一个数组的额外时间消耗只是O(N),这不可能显著影响运行时间。这个问题是空间问题。
堆排序的图解推荐一篇博客讲的很详细,这里我们只是用C语言来实现一下。
原文地址:https://www.cnblogs.com/chengxiao/p/6129630.html。
我们要升序,所以初始化为最大堆
void percdown(int a[], int i, int n)
{
int child; //孩子结点
int temp; //临时变量
/*对堆进行整理*/
for(temp = a[i]; i * 2 + 1 < n; i = child)
{
child = i * 2 + 1; //当前结点的左孩子结点
if(child != n - 1 && a[child] < a[child + 1]) //比较左孩子和右孩子谁大
child++;
if(temp < a[child])
a[i] = a[child];
else
break;
}
a[i] = temp;
}
void heapsort(int a[], int n) //n为数组长度
{
int i;
/*初始化堆*/
for(i = n / 2; i >= 0; i--)
percdown(a, i, n);
/*排序*/
for(i = n - 1; i >=0 ; i--)
{
swap(&a[0], &a[i]); //将堆顶元素沉入堆底,并且将堆长度减1
percdown(a, 0, i);
}
}
int main()
{
int a[10] = {1, 3, 2, 9 ,6 ,8, 7, 5, 0, 4};
int n = 10;
printf("原数组为: ");
for(int i = 0; i < 10; i++)
printf("%d\t", a[i]);
printf("\n");
heapsort(a, n);
printf("排序后的数组为: ");
for(int i = 0; i < 10; i++)
printf("%d\t", a[i]);
printf("\n");
}
运行结果如下