1. 题目描述
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2. 理清思路
2.1 堆排序的基础概念
2.1.2 完全二叉树
如果二叉树的深度为n,则除第n层外其余所有层节点都有2个子节点,且叶子节点从左到右依次存在。如下图所示。
2.1.3 大根堆
在完全二叉树的基础上,每个节点都要大于等于左右节点,假设K表示节点,则大根堆满足 Ki >= K2i && Ki >= K2i+1 ,1 <= i <= (n/2) ,n代表节点个数,如图所示:
2.1.4 小根堆
在完全二叉树的基础上,每个节点都要小于等于左右节点,假设K表示节点,则大根堆满足 Ki <= K2i && Ki <= K2i+1 ,1 <= i <= (n/2) ,n代表节点个数, 如图所示:
堆排序的基本思想
堆排序(Heap Sort)就是利用堆进行排序的算法,它的基本思想是:
- 将待排序的序列构造成一个大顶堆(或小顶堆)。
- 此时,整个序列的最大值就是堆顶的根结点。将它移走(就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值)。
- 然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的此大值。
- 如此反复执行,便能得到一个有序序列了。
3. 代码实现
void swap(int k[], int i, int j)
{
int temp;
temp = k[i];
k[i] = k[j];
k[j] = temp;
}
/*
@k:数组首地址
@s:双亲
@n:待构造的节点个数
*/
void HeapAdjust(int k[], int s, int n)
{
int i, temp;
temp = k[s];//固定住双亲节点
for( i=2*s; i <= n; i*=2 )//2*s 表示左孩子 , 2*s + 1 表示右孩子
{
if( i < n && k[i] < k[i+1] )
{
i++;//指向右孩子
}
if( temp >= k[i] )//若双亲大于左孩子或右孩子,则退出循环,往根节点方向继续构造
{
break;
}
k[s] = k[i];//若双亲小于左孩子或右孩子,则双亲替换为左孩子或右孩子,此时的双亲
s = i;//并且把双亲的下标也替换掉。
}
k[s] = temp;//把开始保存的双亲替换掉左孩子或右孩子。
}
/*从下往上,从左到右构造大根堆*/
void HeapSort(int k[], int n)
{
int i;
/*第一次构造大根堆*/
for( i=n/2; i > 0; i-- )// n/2 表示拿到倒数第二层左节点的下标
{
HeapAdjust(k, i, n);//构造大根堆
}
/*根节点和第(n-i)个节点交换,并将 (n-i)个节点重新构造大根堆*/
for( i=n; i > 1; i-- )
{
swap(k, 1, i);//根节点和第(n-i)个节点交换
HeapAdjust(k, 1, i-1);//将 (n-i)个节点重新构造大根堆
}
}
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
int i = 0;
int *ans;
if(arr == NULL)
{
*returnSize = 0;
return NULL;
}
ans = (int *)malloc(sizeof(int)*(arrSize+1));
ans[0] = -1;
for(i = 1; i <= arrSize; i++)
{
ans[i] = arr[i-1];
}
HeapSort(ans,arrSize);
*returnSize = k;
return &ans[1];
}