
水车老大
昵称就不填了
- 组别:超级版主
- 性别:
- 来自:
- 积分:1418
- 帖子:1329
- 注册:
2007-05-22
|
C语言基础2——初级程序结构的构思
C语言基础2——初级程序结构本文章版权属于 http://yzfy.org 雨中飞燕之家论坛所有,转载请务必注明出处 之前的C语言基础帖子里讲解过int,char和double, 结构讲了分支和循环,现在再来更深入的讲解。一.更简洁的while循环其实你要是弄懂了for,那while循环的理解只要花你几秒钟。while(表达式) { //这里写循环的内容 }和for( ; 表达式;) { //这里写循环的内容 }两者完全等价。不过有一点不同是,for里的表达式可以忽略, 忽略的话就作为true处理,而while里的表达式不可以忽略。while还有一种循环方式:do { //这里写循环的内容 }while(表达式);这种循环方式不能直接改写成for。如果要硬改成for的形式, 那就是://这里写循环的内容,要和for里面的一样 for( ; 表达式;) { //这里写循环的内容 }可以看得出如果需要循环的内容至少要执行一次的情况下,采用 do-while结构可以使代码更为简洁。二.更有效地运用循环这里结合一些实例来介绍:题目1. 因子分解:输入:45 输出:3,3,5,这时最常规的思维,当然就是一个一个数去除啊, 你可能会立刻写出:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { // } return 0; }好了,写到这里你会不会卡在这里?怎么知道一个数除以另一个 数的结果是没有小数的或者要算出余数呢?判断一个数是不是整数 你可能会想if((int)f == f),其中f是浮点数,用强制类型转换 (int)f强制把f转化为int整数类型。但你要注意这样写是不好的, 原因以后解释。这里更有效的方法是使用%运算符。 %运算符就是求余数,a%b就是a除以b所得到的余数。 知道这一点后,然后你可能会写成:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { if(n % a == 0) // 要余数为0 { printf("%d,", a);// 输出成功试除的这个数 } } return 0; }看起来貌似已经成功了,真是这样吗?运行一下试试看! 输入6,输出2,3,6 (好像是对的) 输入12,输出2,3,4,6,12 (明明变成了输出所有大于1的约数) 知道不知道问题是什么?题目是说因子分解,现在却是输出了约数。
现在你应该考虑的是你平时是怎么进行因子分解的: 2|30 3|15 5|5 1 所以输入30应该输出2,3,5 和刚刚的代码的过程有什么差别之处? 很明显的差别是试除的时候,并不是用最原始的数,是用的上一次 除法算出来的结果,每找到一个能整数的数以后, 就需要n = n/a;这样。然后,你可能会把代码写成:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { if(n % a == 0) // 要余数为0 { printf("%d,", a);// 输出成功试除的这个数 n = n/a; // 去掉a这个因子 } } return 0; }貌似成功了,是吗?再多找点数试试: 输入600,输出2,3,5,10 明显不对啊,问题出在哪里?你再自己手算一下: 2|600 2|300 2|150 3|75 5|25 5|5 1 恍然大悟了没?那个a不能只试除一次。可能你又会问, 但那个a每循环一次就自加一次啊? 但代码是活的,你可以不让它每次都自增,你完全可以把++a删掉。 但你可能又问删掉了那怎么控制循环结束? 这时,你应该再回想你刚刚是怎么手算的,什么时候用2试除, 什么时候用更大的数试除。想明白逻辑关系之后,你就会知道, 如果当前试除的这个数不能整除,才去试除下一个数, 在原来的代码怎么加不能整除的条件?很简单,在那个if块的后面 再加一个else就可以了。完整正确代码如下:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ) // a用于试除,这里不写a自加的代码 { if(n % a == 0) // 要余数为0 { printf("%d,", a);// 输出成功试除的这个数 n = n/a; // 去掉a这个因子 } else { ++a; // 试除失败,试下一个数 } } return 0; }本文章版权属于 http://yzfy.org 雨中飞燕之家论坛所有,转载请务必注明出处 题目2: 3n+1 (详见 http://yzfy.org/bbs/viewthread.php?tid=115) 这里为了讲解,只要你算出一个数,把变化过程输出来,也就是: 输入:3 输出:3,10,5,16,8,4,2,1
这里你明显会发现循环次数不确定,在阅读了本文第一点后, 你可能很自然地想到用while来控制这个循环:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要计算的n while(n != 1) // n最后要变成1才退出 { // } return 0; }首先第一反应就要写出这个框架。然后再在while里头做文章。 这个题非常简单,直接判断被2除的余数是不是0,分开来处理:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要计算的n while(n != 1) // n最后要变成1才退出 { if(n % 2 == 0) { // } else { // } } return 0; }接着按题意,在中间添油加醋:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要计算的n while(n != 1) // n最后要变成1才退出 { if(n % 2 == 0) { n = n/2; } else { n = 3*n + 1; // 千万别写成3n + 1,这是低级错误 } } return 0; }最后,还要输出。你可能问输出写在哪里好?应该怎么去写? 如果把那个处理的if..else看成一块,输出的地方有两个选择, 写在if前面或者整块的后面。两种写法有区别不?千万别以为 是一样,运行是按顺序的,在中间过程里的n改变了,放在前 和后的结果肯定不同。你要是动手实验一下, 加一个printf("%d,",n); 分别放在前面和放在后面的话,你就会发现,输入3, 放前面的输出:3,10,5,16,8,4,2 放后面的输出:10,5,16,8,4,2,1 两种放法和要求相比都少了一个数,分别少了最后面和最开头 的数。你可能还会问,这错在哪里了,怎么才能把少了的数也 给输出来啊?
那我说,这还算错吗?你缺少了哪个就补上那个嘛! 你少了最开头的,就在while前面加printf("%d,",n); 少了最后面的,就在while后面面加printf("%d,",n);或者 printf("1,"); 反正最后n也是1嘛!题目3.素性判断输入一个大于1的整数,判断这个数是不是质数,输出yes或者no 输入:41 输出:yes
你可能马上想到,这和刚刚的第一题不是差不多吗?的确差不多, 你可能也马上写出:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { if(n % a == 0) // 余数为0就是说不是素数了 { // } } return 0; }好了,这里很明显,中间要输出no,你可能接着写成:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { if(n % a == 0) // 余数为0就是说不是素数了 { printf("no\n"); } } return 0; }好了,问题来了哪里是输出yes的地方? 你可能问写成这样可以不:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { if(n % a == 0) // 余数为0就是说不是素数了 { printf("no\n"); } else { printf("yes\n"); } } return 0; }你运行一下就发现,输出了很多行yes和no的东西,明显不对。 很明显的一点,你要是找到了一个因子,输出了no以后,后面 你并不需要再循环下去了。现在你可能写成:#include <stdio.h> int main(void) { int n; scanf("%d", &n); // 输入要分解的n for(int a=2; a<=n; ++a) // a用于试除 { if(n % a == 0) // 余数为0就是说不是素数了 { printf("no\n"); break; } else { printf("yes\n"); } } return 0; }总该可以了吧!运行一下看看!输入13,输出了很多行的yes, 再加一行no。晕了没?从结果你可以看出,只要找到了一个不能 整数的数,他就输出一个yes,导致大量输出yes。最后一个no也 很稀奇,这个no是怎么来的?留意一下那个for循环,中间那个 循环条件,写的是什么来着? 找到问题以后就要解决问题,首先肯定不能在for里面输出yes, 因为你要判断完2到n-1之间的数(注意不是2到n),所以你要把 输出yes的语句放在外面。然后要怎么判断要不要输出?其中一个 办法是做标记,加一个变量,记录着有没有输出过no,没有的话 才输出yes。代码可能改写如下:#include <stdio.h> int main(void) { int n; int iMark = 0; // 做标记 scanf("%d", &n); // 输入要分解的n for(int a=2; a<n; ++a) // a用于试除,注意a<n { if(n % a == 0) // 余数为0就是说不是素数了 { printf("no\n"); iMark = 1; // 标记输出过no了 break; } } if(iMark == 0) // 如果标记是0,就是没输出过no { printf("yes\n"); } return 0; }貌似是个不错的想法,输出结果也完全正确了。 但这样就满足了吗,可不可以不加那个变量? 我告诉你,完全可以不加,那个变量可以用a来代替。 怎么代替呢?如果找到了一个因子,就会执行break。 执行break语句的时候,a<n这个条件一定是满足的。 而如果一个因子也没找到的话,最后会因为a<n这个条件不满足 而跳出循环,所以可以改写如下:#include <stdio.h> int main(void) { int n; int a; // 注意变量的作用域的问题 scanf("%d", &n); // 输入要分解的n for(a=2; a<n; ++a) // a用于试除,注意a<n { if(n % a == 0) // 余数为0就是说不是素数了 { printf("no\n"); break; } } if(a >= n) // 和a<n相反就可以了,或者可以更直接用a==n { printf("yes\n"); } return 0; }
|