第5章 循环结构程序设计
第5章 循环结构程序设计. 循环结构概述 goto 语句及用 goto 语句构成循环 三种循环 while 、 do-while 、 for 循环的嵌套 几种循环的比较 Break 语句和 continue 语句 程序举例. 5.0 循环结构导入 在程序设计中含有 循环 结构的问题是大量存在的. 对输入的 10个整数求和,怎么编程? 对输入的 100个整数求和,怎么编程?. 以上问题 可以引出一个概念“ 循环 ”, 简单而言: 循环 就是不断反复地执行同一段程序。. 5.1 概述. 不提倡. C 语言可实现的循环:
第5章 循环结构程序设计
E N D
Presentation Transcript
第5章 循环结构程序设计 • 循环结构概述 • goto语句及用goto语句构成循环 • 三种循环while、do-while、for • 循环的嵌套 • 几种循环的比较 • Break语句和continue语句 • 程序举例
5.0 循环结构导入 在程序设计中含有循环结构的问题是大量存在的 对输入的10个整数求和,怎么编程? 对输入的100个整数求和,怎么编程? 以上问题可以引出一个概念“循环”, 简单而言:循环就是不断反复地执行同一段程序。
5.1 概述 不提倡 • C语言可实现的循环: • 用goto 和 if 构成循环 • while 语句 • do ~ while 语句 • for 语句 当型循环——先判断后执行 直到型循环——先执行后判断 多功能
5.2 goto语句及用goto语句构成循环 一般形式: 语句标号: 1.用于定义程序中的某个位置。 2.用标识符表示,开头不能是数字。 如:goto 3t; 错误 goto t3; 正确 3.只能加在可执行语句前面。 语句标号:语句 ….….. goto 语句标号; 说明: • 1. 功能是无条件转到语句标号处。 • 2. 用途: • 与if语句一起构成循环结构; • 从循环体中跳到循环体外。 • 3. 因为滥用goto语句会破坏结构化,所以应限制使用。
循环条件 循环初值 循环变量增值 循环终值 循环体 5.2 goto语句及用goto语句构成循环 用if语句和goto语句构成循环 例1计算 分析:这是一个累加求和的问题 sum=1+2+3+…+100 #include <stdio.h> main( ) { int i,sum=0; i=1; loop: if(i<=100) { sum+=i; i++; goto loop; } printf("%d",sum); } sum=sum+1=1 sum=sum+2=3 sum=sum+3=6 sum=sum+4=10 …… sum=sum+100=5050
假(0) 表达式 真(非0) 循环体 表达式 循环体 5.3 while 语句 while(表达式) 循环体语句 一般形式: 用while语句实现当型循环 先判断表达式,后执行循环体。 执行过程: 当表达式的值为真(非0)时,执行其中的内嵌语句(循环体),然后回过头来再判断表达式的值,如此重复;当表达式为假(0)时结束循环。
5.3 while 语句 while(表达式) 循环体语句 一般形式: • 注意: • 循环体有可能一次也不执行。 • 循环前,必须给循环控制变量赋初值。 • 若循环体包含一条以上的语句,应以复合语句形式出现。 • 循环体中,必须有改变循环控制变量值的语句(使循环趋向结束的语句)。 • 无限循环: while(1)循环体; • 循环体为空 如: • while((c=getchar())!='A'); 从键盘输入字符直到输入A为止 • 等价于: c=getchar( ); • while (c!='A')c=getchar( ); 例如:k=1;s=0; while ( k<=100){s=s+k;k++;} 循环体 循环控制变量 循环控制表达式
s=0 , k=1 当k≤100 s=s+k k=k+1 例1 用while语句计算 分析:累加求和的问题 s=1+2+3+…+100 #include <stdio.h> main( ) { int k,s; s=0;k=1; while(k<=100) { s=s+k; k++; } printf("s=%d",s); } s=0 s=s+1 s=s+2 s=s+3 …… 注意:s、k的初值及位置 s=s+k 和的新值 和的当前值 求和项当前值
用do-while语句实现直到型循环 循环体 真(非0) 表达式 表达式 循环体 假(0) ;不可省略! 切记!! 5.4 do-while 语句 do 循环体语句 while(表达式); 一般形式: 先执行循环体,后判断表达式。 循环体至少执行一次。 do-while语句可转化成while语句。 执行过程: 执行循环体,当表达式的值为真(非0)时,再次执行循环体,如此重复;当表达式为假(0)时结束循环。
s=0, k=1 s=s+k k=k+1 当k<=100 例1 用do-while语句计算 #include <stdio.h> main( ) { int k,s; s=0;k=1; do { s= s+k; k++; }while(k<=100); printf("s=%d",s); } #include <stdio.h> main( ) { int k,s; s=0;k=1; while(k<=100) { s=s+k; k++; } printf("s=%d",s); } 先判后做 先做后判
适合于循环次数确定或不定 求解表达式1 假(0) 表达式2 真(非0) 循环体语句 求解表达式3 下一语句 5.5 for 语句 for(表达式1;表达式2;表达式3) 循环体语句 一般形式: 执行过程: • 说明: • 表达式1在进入循环之前求解(循环变量赋初值) • for语句可转化成while语句。
5.5 for 语句 for(表达式1;表达式2;表达式3) 循环体语句 一般形式: for(循环变量赋初值;循环条件;循环变量增值) 循环体语句 最简单的应用形式: 例如: for(i=1;i<=100;i++) sum+=i;
表达式1 { 表达式3; } for循环的其他形式: 表达式2为空,则不判断循环条件,成无限循环 这个分号不能移走 • for( 表达式1 ; 表达式2 ; 表达式3 ) 循环体语句 表达式3可以移到循环体语句的后边 这个分号不能丢掉
for循环的说明: 1. 表达式1省略时,应在for前给循环变量赋初值。 如:k=1; for( ; k<=3; k++)s=s+k; 2. 表达式2省略时,不判断循环条件,将成为 “死循环”,需要在循环体中引入break语句以退出循环 3. 表达式3省略时,循环体内应有使循环条件改变的语句。 如:for(k=1;k<=3; ) { s=s+k; k++; }
for循环的说明: 4. 同时省略表达式1和表达式3,只有表达式2,此时相当于while语句。如 k=1; for( ;k<=100; ) { s=s+k; k++; } k=1; while (k<=100) {s=s+k; k++; } 5. 表达式2一般是关系表达式或逻辑表达式,但也可以是数值表达式或字符表达式,只要其值不等于0就执行循环体。 如: for(k=1;k-4;k++)s=s+k; 仅当k的值等于4时终止循环。k-4是数值表达式。
例1 用for语句计算 #include <stdio.h> main( ) { int k,s; s=0; for(k=1;k<=100;k++) s=s+k; printf("s=%d",s); } #include <stdio.h> main( ) { int k,s; s=0;k=1; do { s= s+k; k++; }while(k<=100); printf("s=%d",s); } #include <stdio.h> main( ) { int k,s; s=0;k=1; while(k<=100) { s=s+k; k++; } printf("s=%d",s); } 先判后做 先做后判
5.6 循环的嵌套 1. 一个循环体内包含着另一个完整的循环结构,就称为嵌套循环。 2. 内嵌的循环中又可以嵌套循环,从而构成多重循环。 3. 三种循环可以相互嵌套。下面几种都是合法的嵌套形式: (1) while( ) { …… while() { …… } …... } (2) do { …… do { …… }while( ); …... }while( ); (3) for( ) { …… for(; ;) { …… } ……. } (4) while( ) { …… do { …… }while( ); ……. } (5) for( ; ;) { …… while() { …… } …... } (6) do { …… for(; ;) { …… } …... }while( );
循环嵌套的说明: 1. 嵌套的循环控制变量不应同名,以免造成混乱。 2. 内循环变化快,外循环变化慢。 例如:for(i=1;i<=9;i++) { for(j=1;j<=i;j++) printf("%1d*%1d=%2d ",i,j,i*j); printf("\n"); } 3. 正确确定循环体。 4. 循环控制变量与求解的问题挂钩。
例2试找出满足下列条件的所有的三位数 ①其百位数不大于2; ②若将个位与百位对换,得到的三位数是原三位数的两倍多。 分析: • 用三重循环的循环控制变量分别表示百位数、十位数和个位数 • 百位数i取值1~2 • 十位数j取值0~9 • 个位数k取值2~9 • n=100*i+10*j+k m=100*k+10*j+i • 若m>2*n并m<3*n 则n为满足条件的三位数 main( ) { int i,j,k,n,m,s=0; for(i=1;i<=2;i++) for(j=0;j<=9;j++) for(k=2;k<=9;k++) { n=100*i+10*j+k; m=100*k+10*j+i; if(m>2*n &&m<3*n) { s++; printf("%d ",n); if(s%10==0)printf("\n"); } } }
例2试找出满足下列条件的所有的三位数 ①其百位数不大于2; ②若将个位与百位对换,得到的三位数是原三位数的两倍多。 分析: • 用一重循环的循环控制变量i表示原来的三位数 i=101~299 • 百位数n1=i/100 • 十位数n2=(i-n1*100)/10 • 个位数n3=i-100*n1-10*n2 • m=100*n3+10*n2+n1 • 若m>2*i并m<3*i 则i为满足条件的三位数 main( ) { int i,n1,n2,n3,m,s=0; for(i=101;i<=299;i++) { n1=i/100; n2=(i-n1*100)/10; n3=i-100*n1-10*n2; m=100*n3+10*n2+n1; if(m>2*i&&m<3*i) { s++; printf("%d ",i); if(s%10==0)printf("\n"); } } }
循环控制变量初始化 也可在for前 while(表达式) 循环体语句 for(表达式1;表达式2;表达式3;) 循环体语句 do 循环体语句 while(表达式); 也可在循环体内 循环控制条件 改变循环控制变量值 5.7 几种循环的比较 1. 不提倡用goto构成循环。 2. 关于循环的控制:
5.7 几种循环的比较 3. 一种循环可以解决的问题,使用另外两种同样可行,只是方便程度不同。 4. while循环一般用于循环次数不定的情况,for循环一般用于循环次数确定的情况(也可以用于循环次数不定的情况),do-while循环一般用于至少需要执行一次的情况。 5. for循环和while循环是先判断条件是否为真,再执行循环体,因此,可出现循环一次也不执行的情况;do-while循环是先执行循环体,再判断条件是否为真,因此,循环体至少执行一次。 6. while循环、 do-while循环、 for循环,可以用break语句跳出循环,用continue语句结束本次循环。
5.8 break语句和continue语句 break语句: break; 功能:跳出switch结构;在循环语句中,终止并跳出循环体。 • 说明: • break只能终止并跳出最近一层的结构。 • break不能用于循环语句和switch语句之外的任何其它语句之中。 #define PI 3.1415 main( ) { int r; float area; for(r=1;r<=10;r++) {area=PI*r*r; if(area>100) break; printf("r=%d,area=%.2f\n",r,area); } } 运行结果: r=1,area=3.14 r=2,area=12.57 r=3,area=28.27 r=4,area=50.26 r=5,area=78.54 例3计算r=1到r=10的圆面积,直到面积大于100时停止。
此法更佳 if(n%3)printf("%d",n); 5.8 break语句和continue语句 continue语句: continue; 结束本次循环, 循环体的剩余语句被忽略, 执行表达式3,进入下一循环。 continue总是作if的内嵌语句。 功能:结束本次循环,跳过循环体中尚未执行的语句,进行下一次是否执行循环体的判断。 main( ) { int n; for(n=100;n<=200;n++) { if(n%3==0) continue; printf("%d ",n); } } • 说明: • continue仅用于循环语句中。 例4把100~200之间不能被3整除的数输出 。
t=1,pi=0,n=1.0,s=1 当|t|1e-6 pi=pi+t n=n+2 s=-s t=s/n pi=pi*4 输出pi 5.9 程序举例 例5 用以下公式计算л的值,直到最后一项的绝对值小于1e-6为止。 分析: 分子 s:1,-1,1,-1… 分母 n:1,3,5,7,... 1.每项的分母,等于前一项分母加2,用n=n+2实现,n的初值为1。 2.每项的符号交替变化,用 s=-s实现,s的初值为+1(第一项为正)。 3.根据上述,每一项的值 t = s/n,第一项的值为1。
t=1,pi=0,n=1.0,s=1 当|t|1e-6 pi=pi+t n=n+2 s=-s t=s/n pi=pi*4 输出pi 5.9 程序举例 例5 用以下公式计算л的值,直到最后一项的绝对值小于1e-6为止。 # include <math.h> main( ) { int s=1; float n, t, pi; t=1.0; n=1.0; pi=0.0; while (fabs(t)>=1e-6) { pi=pi+t; n=n+2; s=-s; t=s/n; } pi=pi*4; printf("pi=%10.6f\n ",pi); }
f1=1, f2=1 for i=1 to 20 输出f1,f2 f1=f1+f2 f2=f2+f1 例6 求Fibonacci数列:1,1,2,3,5,8,…的前40项。递推公式: 分析: 1. 每次计算并输出两项f1, f2 ,共进行20次循环; 2. 输出f1, f2后,计算得到新的f1, f2: f1=f1+f2 f2=f2+f1 --------迭代算法 3. 此数列增长很快,输出宜用长整型(%ld),每行输出四个数,即每输出4个数后输出一个换行符(\n)。
f1=1, f2=1 for i=1 to 20 输出f1,f2 f1=f1+f2 f2=f2+f1 例6 求Fibonacci数列:1,1,2,3,5,8,…的前40项。递推公式: main( ) { long int f1,f2; int i; f1=1; f2=1; for(i=1; i<=20; i++) { printf("%12ld %12ld", f1,f2); if(i%2==0) printf("\n"); f1=f1+f2; f2=f2+f1; } }
1 5 34 233 1597 10946 75025 514229 3524578 24157817 1 8 55 377 2584 17711 121393 832040 5702887 39088169 2 13 89 610 4181 28657 196418 1346269 9227465 63245986 3 21 144 987 6765 46368 317811 2178309 14930352 102334155 例6 求Fibonacci数列:1,1,2,3,5,8,…的前40项。递推公式:
例7 搬砖问题: 36块砖36人搬,男搬4女搬3、小孩两个抬1砖,要求一次全搬完,问男、女、小孩各若干? 穷举算法 分析: 设男人、女人、小孩人数各为men,women,children, 则可得如下方程: 4*men+3*women+children/2=36 men+women+children=36 下面考虑如何寻找另外的约束条件: 按常识,men、women、children都应为正整数, 且它们的取值范围分别应为: men:0~8 (假设36块砖全由men搬,最多需8人) women:0~11(假设36块砖全由women搬,最多需11人) children:0~36(假设36块砖全由children搬,最多为36人)
例7 搬砖问题: 36块砖36人搬,男搬4女搬3、小孩两个抬1砖,要求一次全搬完,问男、女、小孩各若干? 本题的细化过程如下: 首先从1开始,列举men的各个可能值,在每个men值下找满足两个方程的一组解。 算法如下: for(men=1;men<=8;men++) { s1:找满足两个方程的解的women,children s2:输出一组解 } 下面进一步用细化法来表现S1: for(women=1;women<=11;women++) { s1.1 找满足方程的一个children s1.2 输出一组解 }
例7 搬砖问题: 36块砖36人搬,男搬4女搬3、小孩两个抬1砖,要求一次全搬完,问男、女、小孩各若干? 由于每个men与每个women都可以按下式 children=36-men-women 求出一个children。 因此,只要该children满足另一个方程 4*men+3*women+childs/2=36 便可以得到一组满足题意的解men、women、children。 故S1.1与S1.2可以写为: children=36-men-women; if(4*men+3*women+children/2==36) printf("%d%d%d\n",men,women,children);
例7 搬砖问题: 36块砖36人搬,男搬4女搬3、小孩两个抬1砖,要求一次全搬完,问男、女、小孩各若干? main( ) { int men,women,children; printf("\nmen***women***children\n"); for(men=1;men<=8;men++) {for(women=1;women<=11;women++) { children=36-men-women; if(4*men+3*women+children/2==36) { printf("%d******",men); printf("%d******", women ); printf("%d******\n", children); } } } }
例7 搬砖问题: 36块砖36人搬,男搬4女搬3、小孩两个抬1砖,要求一次全搬完,问男、女、小孩各若干? 运行结果 men***women***children 1******6******29****** (这是一组错误解) 3******3******30****** 原因:丢失重要条件:children应该能够被2整除。 修改如下: if(4*men+3*women+children/2.0==36) 或 if (children%2==0&&4*men+3*women+children/2==36)
判断方法----让m依次被2,3,4,… ,除,如果m能被其中的任何一个整数整除,则不是素数。 定义变量m,i,k 读入m k=sqrt(m) for(i=2;i<=k;i++) m被i整除 假 真 用break 结束循环 i > k 假 真 输出:m不是素数 输出:m是素数 例7 判断正整数m是否为素数。 什么是素数----只能被自身和1整除的自然数。 #include<math.h> main( ) { int m,i,k; printf("\nInput m:\n"); scanf("%d",&m); k=sqrt(m); for(i=2; i<=k; i++) if (m%i==0) break; if (i>k) printf("%d is a prime\n",m); else printf("%d is not a prime\n",m); }
增加 int n=0; 删除此行 删除此行 for(m=101; m<=200; m+=2) 改最后2行 例8 求100~200之间的全部素数。 • 在上题的基础上,外层增加一个嵌套的for循环即可。 • 用n作素数个数的计数,以控制每行输出10个数。 #include<math.h> main( ) { int m,i,k,n=0; for(m=101; m<=200; m+=2) {k=sqrt(m); for (i=2;i<=k;i++) if (m%i==0) break; if(i>k){printf("%4d",m); n=n+1; if(n%10==0)printf("\n"); } } } #include<math.h> main( ) { int m,i,k; printf("\nInput m:\n"); scanf("%d",&m); k=sqrt(m); for(i=2; i<=k; i++) if (m%i==0) break; if (i>k) printf("%d is a prime\n",m); else printf("%d is not a prime\n",m); }
替换为 int row, col; 替换为 {for (col=1;col<=5-row;col++) printf(" "); printf("********\n"); } 例9 打印图形。 • 分析: • 1.每行的起始位置不同,空格数递减1。 • 2.每行的字符数相同。 • 3.用二重循环实现: • 外循环控制输出行数; • 内循环控制输出的空格数。 分析: 1.图形每行的起始位置相同。 2.每行的字符数相同。 3.用一重循环控制输出行数即可。 main( ) {int row; for(row=1;row<=5;row++) printf("********\n"); } main( ) {int row, col; for(row=1;row<=5;row++) {for (col=1;col<=5-row;col++)printf(" "); printf("********\n"); } }
例9 打印图形。 请思考:若起始位置为20,程序如何修改? • 分析: • 1.每行的起始位置不同, • 空格数:5-row • 2.每行的字符数不同, • 字符数:2*row-1 • 3.用二重循环实现: • 外循环控制输出行数; • 两个并列内循环控制输出每行的空格数和字符数 main( ) {int row, col; for(row=1;row<=5;row++) { for (col=1;col<=5-row;col++)printf(" "); for (col=1;col<=2*row-1;col++)printf("*"); printf("\n"); } }
例10 计算s=1!+2!+3!+…+n!,n从键盘输入 。 分析: 1.需求解阶乘i! 2.需求解阶乘之和∑i! 3.一重(或二重)循环. main( ) { int i,n; float sum=0, term=1; /* int,long,float */ printf("\ninput n: "); scanf("%d",&n); for(i=1;i<=n;i++) { term= term* i; /*求i的阶乘*/ sum= sum+ term; /*累加到sum中*/ } printf("\nsum=%f\n",sum); /* %d,%ld.%f */ } main( ) { int i,j,n; float sum, term; printf("\ninput n: "); scanf("%d",&n); for(sum=0,i=1;i<=n;i++) { for (term=1, j=1;j<=i;j++) { term*=j; } sum= sum+ term; } printf("\nsum=%f\n",sum); }
例11 循环嵌套,输出九九表。 main( ) { int m,n; for(m=1;m<=9;m++) { for (n=1;n<=m;n++) printf("%1dx%1d=%-4d",m,n,m*n);/*-的作用*/ printf("\n"); } }
注意:getchar( )仅当遇到回车符时才开始执行,从计算机缓冲区中取字符。 如输入Ok! 则输出Ok! 例12 读入一个字符,当它不是回车符时就输出。 #include<stdio.h> /* 此行必须有 */ main( ) { char c; while ( (c=getchar( ))!='\n') putchar(c); putchar('\n'); } 如何仅输出字母? 如输入1234china 则输出china #include<stdio.h> /* 此行必须有 */ main( ) { char c; while ( (c=getchar( ))!='\n') if(c>='a' && c<='z' || c>='A' && c<='Z') putchar(c); putchar('\n'); }
小 结 • 三种循环while、do-while、for的应用范围、各种循环的完整性 • 循环的嵌套——多重循环 • Break、continue的作用 • 循环的算法:穷举法搬砖问题、迭代法Fibonacci数列 • 循环结构程序设计举例