因为这次的作业仍是补充函数的定义,所以下面将只给出这一部分的代码。
Lab0901,EOJ3172。
将数字转换成字符串,我的第一反应就是sprintf()。
{ //TODO: your function definition -- RECURSIVE SOLUTION sprintf(s,"%I64d",n); return; }
可惜竟然WA了,究其原因是要写 %I64d ,而不能只写 %d 。
当然,这道题在这里作为作业,应该是希望我们写成这个样子的:
{ //TODO: your function definition -- RECURSIVE SOLUTION long long int tmp=n; int cnt=0; if (tmp==0) { cnt=1; } else { while (tmp>0){ cnt++; tmp/=10; } } tmp=n; s[cnt]='\0'; for (int i=cnt-1;i>=0;i--){ s[i]=tmp%10+'0'; tmp/=10; } return; }
这里的做法是,首先依次整除10计算位数,然后把每一位保留下来。
当然也可以倒序保存在数组里面,然后再想办法把顺序颠倒过来。
这里注意一个地方,也是我WA过一次的地方,那就是要单独考虑输入n=0这种情形。
也就是说,如果输入的数字是0,那么 while (tmp>0) 是不成立的,所以如果不对tmp==0做单独的考虑,就会导致问题。
除此而外,tmp需要是long long类型,并且s[i]=tmp%10以后还要加上’0’(即48)。
由于题目要求使用递归,所以12月1日在课堂上又写出了下面这段代码,代码与注释一起给出。
#include <stdio.h> #include <string.h> void getreverse(char s[],long long int n){ //用来获得反过来的字符串 if (n) { s[0]=n%10+'0'; //如果n不是0,取出个位存入数组 getreverse(&s[1],n/10); //递归的时候houn整除10 //数组的话,用后面一个单元的地址,也就是s[1]的地址,即&s[1] } else { s[0]='\0'; //做完以后,加上字符串结束符 } return; } void i2a(char s[],long long int n){ if (n) { getreverse(s,n); //如果n不是0,获取反过来的字符串 } else { s[0]='0'; getreverse(&s[1],0); //否则的话,单独处理0字符 } int h=0,t=strlen(s)-1; //h表示head,t表示tail while (h<t){ char c=s[h]; s[h]=s[t]; s[t]=c; h++; t--; } //颠倒 return; }
如果能用全局变量的话,递归的时候传的就是s和n/10就好了,下标用全局的i来控制。
于是就会有形如这样的语句。
s[cnt++]=n%10;
i2a(s,n/10);
如果不能用全局变量,也可以在i2a的参数表中增加一个参数,递归的时候传s和n/10和i+1即可。
于是就会有形如这样的语句。
void i2a(char s[],long long int n, long long int i);
i2a(s,n/10,i+1)
Lab0902,EOJ3173。
二分,一般来说是用循环的。但是题目要求写成递归形式,那么就把代码改一改,很顺利就写出来了。
{ //TODO: your function definition -- RECURSIVE SOLUTION if (L>R) { return -1; } else { int M=(L+R)/2; if (x>a[M]){ binarySearch(a,M+1,R,x); } else if (x<a[M]){ binarySearch(a,L,M-1,x); } else { return M; } } }
Lab0903,EOJ3017。
直接计算出结果,然后统计个数。
{ //TODO: your function definition long long int sum=1; for (int i=1;i<=n;i++){ sum*=i; } int cnt=0; while (!(sum%10)){ cnt++; sum/=10; } return cnt; }
for (int i=1;i<=n;i++){ sum*=i; }
这个for循环是在计算。
while (!(sum%10)){ cnt++; sum/=10; }
这个while循环是在统计个数。
事实上,因为题目的输入数据只到20,所以这题还可以写成这个样子:
{ //TODO: your function definition return n/5; }
至于原因,数学上可以证明,在20以内只要用n整除5(只要判断5的倍数)就可以了。
Lab0904,EOJ3105。
首先给出最中规中矩的做法。
{ //TODO: your function definition int pos = 0, max = 0, len = 0, tmp; for ( int i = 0; i <= strlen ( str ); i++ ) { if ( str[i] != ' ' && i < strlen ( str ) ) { if ( i == 0 || str[i - 1] == ' ') tmp = i; len++; } else { if ( max < len ) { max = len; pos = tmp; } len = 0; } } for ( int i = 0; i < max; i++ ) { result[i] = str[pos + i]; } result[max] = '\0'; return; }
首先说明各个变量的含义。
pos记录最长单词的起始字母位置,max记录最长单词的长度,len记录当前单词的长度,tmp记录当前单词的起始字母位置。
需要注意的一点是,这里的变量是都要赋初值的,这里这个步骤不能省略。
如果没有赋初值,譬如没有对pos赋初值,因为局部变量没有缺省值,而由于if语句的存在,很有可能当函数结束的时候pos并没有被初始化过,于是导致错误。
主要的思路是,依次遍历整个字符串,记录下每个单词(以空格分隔)的长度,从中找出最长的单词,记下pos和max。
实现的时候,很多地方容易出错,下面一一说明。
for ( int i = 0; i <= strlen ( str ); i++ )
这里如果写成
for ( int i = 0; i < strlen ( str ); i++ )
就会导致最后一个单词没有被记录下,也就是,虽然比到了最后一个单词,但是没能把它的信息保存下来。
因此,等号要加上去。
可是,等号加上去的话就会导致死循环,那么下面的判断条件就要加上
&& i < strlen ( str )
这么做还有一个问题,那就是第一个值没有办法记录下来,这里的第一个值指的是第一个单词的第一个开头字母的位置(字符串的0号位)。
当然有一种可能的做法是在声明tmp的时候给它初始化为0,即写成int tmp=0。
我这里采用的方法是
i == 0 ||
这句话不仅保证了当字符串处在开头的时候,就先让tmp=0,于是这里完成的事情与写成int tmp=0完成的事情是一样的。
而且,这句话还可以保证,如果i为0,那么就短路,短路以后就不会去做str[i – 1] ,也就是避免了出现str[-1]这样的局面出现。
后面的代码理解起来没有难度。
for ( int i = 0; i < max; i++ ) { result[i] = str[pos + i]; }
这是在获取结果。
最后要加上’\0’表示字符串结束,否则的话有可能会输出奇怪的字符(因为没有正常截断字符串)。
写完这个程序,再回过头来想一想,如果摆脱作业的要求,不写成函数的形式,我可以用另外一种输入和输出的方式,让程序变得更简单。
#include <stdio.h> #include <string.h> #define N 80 int main() { char st[N+1][N+1]; int cnt=0,max=0,len=0,pos=0; while (scanf("%s",st[cnt++])!=EOF); max=strlen(st[0]); for (int i=0;i<cnt-1;i++){ len=strlen(st[i]); if (max<len) {pos=i;max=len;} } printf("%s\n",st[pos]); return 0; }
由于本题输入的内容已经纯天然地被用空格分隔了,所以我利用一个循环体为空的while循环读入所有的字符串,保存在一个二维char数组里(或者可以认为保存在一个一维的string数组里,只不过这里的每个string就是一个一维的char[]数组)。
全部读入以后,依次遍历每一个数组,很顺利得到答案。
请问防止全局变量风险的方法是每次调用时初始化吗?
@闫 壮全局变量有缺省值,可以不初始化,防止风险的方法是适当地使用const。