目录
思路3(最优解)解决TopK问题:把数组里的前k个数建大堆,剩下的N-k个数在和堆顶比较,比堆顶小就替换,在调堆(向下调整)
思路1:直接按照升序排序,出原数组前K个数
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
//升序
void BubbleSort(int* a, int n)
{
//冒泡排序循环趟数
for (int i = 0; i < n-1; i++)
{
int j = 0;
//每一趟需要交换的次数
for (j = 0; j < n - 1 - i; j++)
{
//两两比较大的就交换
if (a[j] > a[j+1])
{
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
}
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
BubbleSort(arr,arrSize);
//需要注意题上要求开辟空间来返回数组
int* returnarr = (int*)malloc(sizeof(int)*k);
//把数组arr里面前K个数放进开辟的数组里面
for(int i = 0;i < k;i++)
{
returnarr[i] = arr[i];
}
*returnSize = k;
return returnarr;
}
思路2:建一个小堆,出堆里前k个数
这里用的是纯C需要自己写一个堆 ,下面是如何实现一个堆有兴趣的小伙伴可以去看看
https://blog.csdn.net/qq_45229484/article/details/121950193
//小堆
typedef int TypeHeapDate;
typedef struct Heap
{
TypeHeapDate* a;
int size;
int capacity;
}Heap;
void AdjustDownward(int* a, int n, int parent);
void Swap(int* p1, int* p2);
//初始化(创建)堆
void HeapCreate(Heap* php, TypeHeapDate* a,int sn);
void HeapDestroy(Heap* php);
void HeapPush(Heap* php, TypeHeapDate x);
void HeapPop(Heap* php);
TypeHeapDate HeapTop(Heap* php);
int HeapSize(Heap* php);
bool HeapEmpty(Heap* php);
void HeapSort(TypeHeapDate* a, int sn);
void HeapPrint(Heap* php);
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustDownward(int* a, int n, int parent)
{
int child = parent * 2 + 1;
//比较两个(左右)孩子的大小
while (child < n)//在数组下标范围内比较
{
//选出左右孩子较小的那个
if (child + 1 < n && a[child + 1] < a[child])//假如没有右孩子chile可能越界
{
child++;//右孩子比左孩子小 ++child加到右孩子下标
}
//parent和较小的孩子交换
//较小的孩子比父亲小交换,比父亲大跳出(此时已经是小堆)
if (a[child] <a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapCreate(Heap* php, TypeHeapDate* a, int sn)
{
assert(php);
php->a = (TypeHeapDate*)malloc(sizeof(TypeHeapDate)*sn);
if (php->a == NULL)
{
printf("file of malloc");
exit(-1);
}
//拷贝过去 方便后面删除插入(需要开辟空间)
memcpy(php->a,a,sizeof(TypeHeapDate)*sn);
php->capacity = php->size = sn;
//建堆 -》大堆
for (int i = (sn - 1 - 1) / 2; i >= 0; i--)
{
AdjustDownward(php->a,php->size,i);
}
}
void HeapDestroy(Heap* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->capacity = php->size = 0;
}
void AdjustUp(int*a,int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if(a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPush(Heap* php, TypeHeapDate x)
{
assert(php);
//满了需要增容
if (php->capacity == php->size)
{
TypeHeapDate* newspace = (TypeHeapDate*)realloc(php->a,sizeof(TypeHeapDate)*php->capacity*2);
if (newspace == NULL)
{
printf("file of relloc");
exit(-1);
}
php->a = newspace;
php->capacity *= 2;
}
//int push = php->size - 1;
php->a[php->size] = x;
php->size++;
//插入后需要考虑插入数的大小 需要向上调正
AdjustUp(php->a,php->size-1);
}
void HeapPop(Heap* php)
{
assert(php);
//没有数
assert(php->size > 0);
//删除先把第一个和最后一个数交换 重新向下调整
Swap(&php->a[0], &php->a[php->size - 1]);
php->size--;
AdjustDownward(php->a, php->size, 0);
}
TypeHeapDate HeapTop(Heap* php)
{
assert(php);
assert(php->size > 0);
return php->a[0];
}
int HeapSize(Heap* php)
{
assert(php);
assert(php->size > 0);
return php->size;
}
bool HeapEmpty(Heap* php)
{
assert(php);
return php->size == 0;
}
//排升序 -》大堆
void HeapSort(int* a, int n)
{
//建堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDownward(a, n, i);
}
int end = n - 1;
while (end > 0)
{
//最大的和第一个交换
Swap(&a[0], &a[end]);
//向下调整选出次大的
AdjustDownward(a, end, 0);
end--;
}
}
void HeapPrint(Heap* php)
{
for (int j = 0;j < php->size;j++)
{
printf("%d ",php->a[j]);
}
printf("\n");
int num = 0;
int maxnum = 1;
for (int i = 0; i < php->size; i++)
{
printf("%d ",php->a[i]);
num++;
if (num == maxnum)
{
printf("\n");
maxnum *= 2;
num = 0;
}
}
printf("\n\n");
}
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
Heap php;
//小堆
HeapCreate(&php,arr,arrSize);
int* returnarr = (int*)malloc(sizeof(int)*k);
for(int i = 0;i < k;i++)
{
returnarr[i] = HeapTop(&php) ;
HeapPop(&php);
}
* returnSize = k;
return returnarr;
}
思路1和思路2的缺点( top k问题)
当给出的数(N)非常大,此时思路1和思路2就不适合了,思路1需要开辟N个数的数组,思路2需要创建N个数的堆 二者存在空间浪费严重,甚至不够
例如10亿个整数里,寻找前5个小的数
在海量数据处理中,经常会遇到的一类问题:在海量数据中找出出现频率最高的前k个数,或者从海量数据中找出最大的前k个数,例如10亿个整数里,寻找前K个最小的数 这类问题通常被称为top K问题。
思路3(最优解)解决TopK问题:把数组里的前k个数建大堆,剩下的N-k个数在和堆顶比较,比堆顶小就替换,在调堆(向下调整)
最后堆里就是最小的K个数 这里的核心就是不断调堆
下面是向下调整算法有兴趣的小伙伴们可以去可看看
堆的特殊实现(向下调整算法)及排序_darling-02的博客-CSDN博客
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustDownward(int* a, int n, int parent)
{
int child = parent * 2 + 1;
//比较两个(左右)孩子的大小
while (child < n)//在数组下标范围内比较
{
//选出左右孩子较小的那个
if (child + 1 < n && a[child + 1] > a[child])//假如没有右孩子chile可能越界
{
child++;//右孩子比左孩子小 ++child加到右孩子下标
}
//parent和较小的孩子交换
//较小的孩子比父亲小交换,比父亲大跳出(此时已经是小堆)
if (a[child]> a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
//需要注意K为零时
if(k == 0)
{
*returnSize = 0;
return NULL;
}
//数组里面前K个数建大堆
int* returnarr = (int*)malloc(sizeof(int)*k);
for(int i= 0;i<k;i++)
{
returnarr[i] = arr[i];
}
//建大堆
for(int j = (k-1-1)/2;j >= 0;j--)
{
AdjustDownward(returnarr,k,j);
}
//剩下N-K个数,和堆顶比较,比它小就替换
for(int j = k; j < arrSize;j++)
{
if(returnarr[0]> arr[j])
{
returnarr[0] = arr[j];
AdjustDownward(returnarr,k,0);
}
}
*returnSize = k;
return returnarr;
}