【原创】整数读入优化 C++

一、背景


这是某道题目的状态:

我的代码:


大牛的代码:



我瞬间萌币了!

我有这么慢?Are you kidding me?


点进去一看:

void Read(int & p)
{
	p=0;
	int flag=1;
	char c=getchar();
	while(c<'0' or c>'9') 
	{
		if(c=='-') flag=-1;
		c=getchar();
	}
	while(c>='0' and c<='9')
		p=p*10+(c-'0'),c=getchar();
	p*=flag;
}


这是什么?

原来,这是读入优化啊!


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


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里面。因此,我们需要跳过不是代表数字的字符


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

void Read(int & p)
{
	p=0;
	char c=getchar();
	while(c<'0' or c>'9') c=getchar();
	while(c>='0' and c<='9')
		p=p*10+(c-'0'),c=getchar();
}


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

除了这种,比较常用的是

int Read()
{
	int p=0;
	char c=getchar();
	while(c<'0' or c>'9') c=getchar();
	while(c>='0' and c<='9')
		p=p*10+(c-'0'),c=getchar();
	return p;
}


调用p=Read();即可。

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

所以也可以写成:

void Read(int & p)
{
	p=0;
	char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c))
		p=p*10+(c-'0'),c=getchar();
}

的形式。


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


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

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

这就是文章开头的代码:

void Read(int & p)
{
	p=0;
	int flag=1;
	char c=getchar();
	while(c<'0' or c>'9') 
	{
		if(c=='-') flag=-1;
		c=getchar();
	}
	while(c>='0' and c<='9')
		p=p*10+(c-'0'),c=getchar();
	p*=flag;
}


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


四、验证读入优化的效率

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

文本生成:

#include<cstdio>
int main()
{
	freopen("test.txt","w",stdout);
	const int N=5000000;
	for(int i=1;i<=N;i++)
		printf("%d  ",N);
}

验证程序:

#include<cstdio>
#include<ctime>
#include<algorithm>
#include<windows.h>
#include<iostream>
#include<cctype>
using namespace std;
void s(int & p)
{
	scanf("%d",&p);
}
void c(int & p)
{
	cin>>p;
}
void Read(int & p)
{
	p=0;
	char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c))
		p=p*10+(c-'0'),c=getchar();
}
double t1,t2;
int main()
{
	freopen("test.txt","r",stdin);
	int p;
	
	t1=clock();
	//for(int i=1;i<=5000000;i++) s(p);
	//for(int i=1;i<=5000000;i++) c(p);
	for(int i=1;i<=5000000;i++) Read(p);
	t2=clock();
	
	//printf("scanf took %.2lf second",(t2-t1)/1000);
	//printf("cin took %.2lf second",(t2-t1)/1000);
	printf("Read took %.2lf second",(t2-t1)/1000);
}



结果:

scanf took 0.85 second

cin took 14.63 second

Read took 0.35 second


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






  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值