标题:C语言库函数scanf()解读
水墨不写bug
(图片来源于网络)
正文开始:
Top-K问题是一类问题的统称:
即根据对象的某一属性,找出这个属性最突出的K个对象,并且通常对象的数量极大。
(一)构造对象
为演示方便,通过代码生成一个数据量极大的数据对象:生成1w个数据;如果我们将问题抽象化为找出1w个数据中的最大5个数,那么Top-K问题就切实可见了。
在你的编译器上输入如下代码并运行,你会发现在同目录下多了一个文件:"data.txt"
#include<stdio.h>
#include<stdlib.h>
void CreateNDate()
{
// 造数据
int n = 10000;
srand(time(0));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (fin == NULL)
{
perror("fopen error");
return;
}
for (size_t i = 0; i < n; ++i)
{
int x = rand() % 1000000;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
再打开文件,人为的将5个数改写为最大并做好记录。
(二)建堆并解决过程
打开文件并读入数据,读入并压入k个数据,这时堆内已有k个数据;
接下来每读入一个数据,就需要堆顶部的数据进行比较;
(找出最大K个数:这种情况需要建立小堆,因为小堆的堆顶数据是这个堆里最小的:最小的数与读入的数进行比较。如果读入的数比较大,就将较小的堆顶交换出去;如果读入的数比较小,就保留堆顶的数据)
(找出最小K个数:这种情况需要建立大堆,因为大堆的堆顶数据是这个堆里最大的:最大的数与读入的数进行比较。如果读入的数比较小,就将较大的堆顶交换出去;如果读入的数比较大,就保留堆顶的数据)
这样既能利用堆的高效插入和高效删除,又避免了开辟大量的空间来存储变量。
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int HPDatatype;
typedef struct Heap
{
HPDatatype* a;
int size;
int capacity;
}HP;
void AdgustDown(HPDatatype* a, int n, int parent);
//初始化
void HPinit(HP* php);
//销毁
void HPDestroy(HP* php);
//插入数据
void HPpush(HP* php, HPDatatype x);
//删除数据,规定删除堆顶的数据
void HPpop(HP* php);
//得到堆顶数据
HPDatatype HPtop(HP* php);
//判空
bool HPempty(HP* php);
//数据个数
int HPsize(HP* php);
//初始化
void HPinit(HP* php)
{
assert(php);
php->capacity = php->size = 0;//size指向最后一个元素的下一个
php->a = NULL;
}
//销毁
void HPDestroy(HP* php)
{
assert(php);
php->capacity = php->size = 0;
free(php->a);
}
//交换函数
void Swap(HPDatatype* a,HPDatatype* b)
{
HPDatatype tem = *a;
*a = *b;
*b = tem;
}
//此处模拟实现小堆,小数上移
//参数:
//要插入的数据和孩子节点的数组下标
void AdgustUp(HPDatatype* 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 HPpush(HP* php, HPDatatype x)
{
assert(php);
//如果size == capacity 说明顺序表满了,或者没有一个数据
if (php->size == php->capacity)
{
int Newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDatatype* tem = (HPDatatype*)realloc(php->a, sizeof(HPDatatype)*Newcapacity);
{
if (tem == NULL)
{
perror("realloc fail!");
return;
}
}
php->a = tem;
php->capacity = Newcapacity;
}
//开辟好空间后,插入
php->a[php->size] = x;
php->size++;
//向上调整
AdgustUp(php->a,php->size-1);
}
//向下调整
//参数;
//顺序表,数据总个数,向下调整时目前双亲节点的数组下标
void AdgustDown(HPDatatype* a,int n,int parent)
{
int child = parent * 2 + 1;
//目前假设左孩子小
while (child < n)
{ //如果右孩子存在,并比左孩子小,选择右孩子
if (child + 1 < n && a[child] > a[child + 1])
{
++child;
}
//此时孩子表示较小的孩子
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
//删除数据,规定删除堆顶的数据
void HPpop(HP* php)
{
assert(php);
Swap(&php->a[0], &php->a[php->size - 1]);
--php->size;
AdgustDown(php->a,php->size,0);
}
//得到堆顶数据
HPDatatype HPtop(HP* php)
{
assert(php);
return php->a[0];
}
//判空
//若为空,返回真,否则返回假
bool HPempty(HP* php)
{
assert(php);
return php->size == 0;
}
//数据个数
int HPsize(HP* php)
{
assert(php);
return php->size;
}
int main()
{
//CreateNDate();
FILE* fin = fopen("data.txt", "r");
if (fin == NULL)
{
perror("fopen error");
return;
}
int comp = 0;
HP ph;
HPinit(&ph);
int k = 0;
scanf("%d", &k);
//刚开始push k次,建堆
for (int i = 0; i < k; i++)
{
fscanf(fin, "%d", &comp);
HPpush(&ph, comp);
}
//开始比较,comp与堆顶数据的大小
while (fscanf(fin,"%d",&comp) != EOF)
{
if (ph.a[0] < comp)
{
Swap(&ph.a[0], &comp);
AdgustDown(ph.a,ph.size,0);
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", ph.a[i]);
}
return 0;
}
(注意: 先调用CreateNDate()函数来创建一个数据文件,标记好最大K个数后再运行上述代码来验证。)
完~
未经作者同意禁止转载