利用堆找出最小k个数
思路:
用一个大根堆实时维护数组的前 k 小值。首先将前 k 个数插入大根堆中,随后从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。
复杂度分析
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),其中
n
n
n 是数组 arr 的长度。算法的时间复杂度即排序的时间复杂度。
空间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn),排序所需额外的空间复杂度为
O
(
l
o
g
n
)
O(logn)
O(logn)。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
//建立大堆
void AdjustDown(int* arr, int n,int root)
{
int parent = root;
int child = parent * 2 + 1;
while(child < n)
{
//找出左右子树最大的一个
if(child + 1 < n && arr[child+1] > arr[child])
{
++child;
}
//若子结点比父结点大,则替换,实现大堆
if(arr[child] > arr[parent])
{
int tmp = arr[child];
arr[child] = arr[parent];
arr[parent] = tmp;
//迭代
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
int* smallestK(int* arr, int arrSize, int k, int* returnSize){
*returnSize = k;
if(k == 0)
return NULL;
int* arrAns = (int*)malloc(sizeof(int)*k);
for(int i = 0; i < k; i++)
{
arrAns[i] = arr[i];
}
//构建堆
for(int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(arrAns, k, i);
}
//通过迭代比较大堆的堆顶(即堆的最大值),
//若比堆顶小则替代,最终找出最小的k个数
for(int i = k; i < arrSize; i++)
{
if(arr[i] < arrAns[0])
{
arrAns[0] = arr[i];
//构建大堆
AdjustDown(arrAns, k, 0);
}
}
return arrAns;
}