c++读入优化(整数)

原文链接:https://blog.csdn.net/c20182030/article/details/69525919

一、背景

这是某道题目的状态:

我的代码:

大牛的代码:

我瞬间萌币了!

我有这么慢?Are you kidding me?

扫描二维码关注公众号,回复: 2478494 查看本文章

点进去一看:

 
  1. void Read(int & p)

  2. {

  3. p=0;

  4. int flag=1;

  5. char c=getchar();

  6. while(c<'0' or c>'9')

  7. {

  8. if(c=='-') flag=-1;

  9. c=getchar();

  10. }

  11. while(c>='0' and c<='9')

  12. p=p*10+(c-'0'),c=getchar();

  13. p*=flag;

  14. }

这是什么?

原来,这是读入优化啊!

二、读入优化的原理与实现

C语言和C++库里有很多种输入方式,我们最常用的是scanf和cin。除此之外,还有:

char c[]; gets(c);//读入一行字符串,但是需要注意的是,它只会在遇到'\n'后停止,不会因为超过c的内存上限而停止。所以容易溢出。

char c[]; fgets(c,len,stdin);//读入一行字符串。和gets不同的是,它如果读入了len-1个字符,就会停止。

char c=getchar();//就像scanf一样,只读入一个字符,并返回这个字符,需要#include<cstdio>头文件。与scanf不同的是,它没有缓冲,也就意味着它会更快。

char c=getch();//直接读入一个字符,而不会回显。也就是说,你读入了一个字符,它不会在界面里显现而直接读入getch,getch也会直接返回这个字符。比如说你写了一个程序小游戏,你肯定不希望看到wasdwasdwasd满天飞,所以就用getch。需要#include<conio.h>头文件。同样,它没有缓冲。

等等。

可以看到,getchar比scanf更快。我们可不可以用getchar来改进我们的输入呢?

下面以整数的读入优化为例。其实我也只会整数的读入优化。。。

让我们随便写一个整数:

令p=123456789

如果用getchar,它会读入一个字符‘1’,然后读入'2','3','4'......

怎么产生数字呢?我们用'1'-'0'(实质是ASCII码的运算),结果就是数字1。

同理,char c=getchar(); int k=c-'0'; 就可以得到这个数字k。

现在要把所有的k加到一起,得到p。

让我们一步一步地来:

因为我们一次只读入一位,所以要把这个数拆成一位一位的形式。

最高位,1=1;

前两位,12=1*10+2;

前三位,123=1*100+2*10+3=12*10+3;

前四位,1234=1*1000+2*100+3*10+4=123*10+4;

.......

规律已经很明显了。

每读入一个代表数字的字符c,p=p*10+c-'0';

我们只需要不停地while(c>='0' and c<='9'),并且处理p即可。

问题来了,如果这个c不代表数字,比如说:

123  456

789

123和456中间有空格,456和789之间有换行,怎么处理呢?

因为这里是三个整数,读入了123以后,还剩下“  456”,前两个c=‘ ’肯定不能让c-‘0’算在p里面。因此,我们需要跳过不是代表数字的字符。

我们就可以写出一个基本的整数读入优化:

 
  1. void Read(int & p)

  2. {

  3. p=0;

  4. char c=getchar();

  5. while(c<'0' or c>'9') c=getchar();

  6. while(c>='0' and c<='9')

  7. p=p*10+(c-'0'),c=getchar();

  8. }

要读入整数p,只需要调用Read(p)即可。

除了这种,比较常用的是

 
  1. int Read()

  2. {

  3. int p=0;

  4. char c=getchar();

  5. while(c<'0' or c>'9') c=getchar();

  6. while(c>='0' and c<='9')

  7. p=p*10+(c-'0'),c=getchar();

  8. return p;

  9. }

调用p=Read();即可。

其实(c>='0' and c<='9')也是一个函数isdigit(c),如果c是代表数字的字符,就返回1,否则返回0,需要#include<cctype>头文件。

所以也可以写成:

 
  1. void Read(int & p)

  2. {

  3. p=0;

  4. char c=getchar();

  5. while(!isdigit(c)) c=getchar();

  6. while(isdigit(c))

  7. p=p*10+(c-'0'),c=getchar();

  8. }

的形式。

三、整数读入优化的特殊处理——负数

对于一个负数,读入优化只会读入数字,负号并不会被读入。

所以需要读入负数,要特判这个字符是不是负号,如果是,那么这个值要改成负数。

这就是文章开头的代码:

 
  1. void Read(int & p)

  2. {

  3. p=0;

  4. int flag=1;

  5. char c=getchar();

  6. while(c<'0' or c>'9')

  7. {

  8. if(c=='-') flag=-1;

  9. c=getchar();

  10. }

  11. while(c>='0' and c<='9')

  12. p=p*10+(c-'0'),c=getchar();

  13. p*=flag;

  14. }

但是,这个代码有一个弊端。所有的‘-’都必须代表负号。如果是减号,比如4-3,就会读错。

四、验证读入优化的效率

用freopen生成五百万个数的文本,并分别用scanf,cin和读入优化读入。

文本生成:

 
  1. #include<cstdio>

  2. int main()

  3. {

  4. freopen("test.txt","w",stdout);

  5. const int N=5000000;

  6. for(int i=1;i<=N;i++)

  7. printf("%d ",N);

  8. }

验证程序:

 
  1. #include<cstdio>

  2. #include<ctime>

  3. #include<algorithm>

  4. #include<windows.h>

  5. #include<iostream>

  6. #include<cctype>

  7. using namespace std;

  8. void s(int & p)

  9. {

  10. scanf("%d",&p);

  11. }

  12. void c(int & p)

  13. {

  14. cin>>p;

  15. }

  16. void Read(int & p)

  17. {

  18. p=0;

  19. char c=getchar();

  20. while(!isdigit(c)) c=getchar();

  21. while(isdigit(c))

  22. p=p*10+(c-'0'),c=getchar();

  23. }

  24. double t1,t2;

  25. int main()

  26. {

  27. freopen("test.txt","r",stdin);

  28. int p;

  29.  
  30. t1=clock();

  31. //for(int i=1;i<=5000000;i++) s(p);

  32. //for(int i=1;i<=5000000;i++) c(p);

  33. for(int i=1;i<=5000000;i++) Read(p);

  34. t2=clock();

  35.  
  36. //printf("scanf took %.2lf second",(t2-t1)/1000);

  37. //printf("cin took %.2lf second",(t2-t1)/1000);

  38. printf("Read took %.2lf second",(t2-t1)/1000);

  39. }

结果:

scanf took 0.85 second

cin took 14.63 second

Read took 0.35 second

所以说读入优化还是很快的。

猜你喜欢

转载自blog.csdn.net/GodJing007/article/details/81291439