650 likes | 821 Views
C 语言程序设计. 第三讲 算法基础与程序的基本结构 绍兴文理学院 2014 年 2 月. 本章主要内容. 课外阅读材料 程式设计与流程图 ( 请上网下载 ). 程序设计方法简述 算法与流程图 算法的两种表示法 两种流程图 三种基本结构 选择结构程序设计 if …else 语句 switch 语句 循环结构程序设计 四种循环语句 常用算法 枚举法(穷举法) 归纳法(递推法). 一、程序设计方法简述 1 、计算机处理问题的过程. 【 例一 】 让某学生解方程 ax 2 +bx+c=0 求解过程:.
E N D
C语言程序设计 第三讲 算法基础与程序的基本结构 绍兴文理学院 2014年2月
本章主要内容 课外阅读材料 程式设计与流程图 (请上网下载) • 程序设计方法简述 • 算法与流程图 算法的两种表示法 两种流程图 三种基本结构 • 选择结构程序设计 if …else 语句 switch语句 • 循环结构程序设计 四种循环语句 • 常用算法 枚举法(穷举法) 归纳法(递推法)
一、程序设计方法简述1、计算机处理问题的过程一、程序设计方法简述1、计算机处理问题的过程 • 【例一】让某学生解方程ax2+bx+c=0 求解过程: ①分析问题 这是一个一元二次方程(代数问题,须中学代数知识) ②确定处理方案 用求根公式 ③确定解题步骤 确定a、b、c的值 求出b2 -4ac的值 如果 b2 -4ac>0(双实根) X1=…… X2=…… 如果 b2 -4ac=0(单实根) X1=X2=…… 如果 b2 -4ac<0(双复根) X1=…… X2=…… ④根据上述步骤计算 ⑤写出答案,整理、分析结果
一、程序设计方法简述1、计算机处理问题的过程一、程序设计方法简述1、计算机处理问题的过程 程序测试与修改 2、编程要诀——自顶向下,逐步求精“先大纲,后文章” 如同写文章:分几部分——每部分几个问题——每个问题几点…… 优点:不易顾此失彼;易于检查;减少后期修改工作量 对于面向过程的程序设计语言: 程序=数据结构+算法(做什么,如何做) 对比:文章=材料+构思
一、程序设计方法简述3、程序测试 • 目的 发现程序中的错误(Bug) • 程序错误 ◆语法错误(编译系统检查) ◆逻辑错误(编程人员检查) • 方法与技术 测试是以程序通过了编译,没有语法和连接错误为前提。在此基础上运行一组数据,来检测程序的逻辑错误。这一组测试数据应是以“任何程序都是有错误的”为前提精心设计出来的。它不仅应含有被测程序各种情况下的代表性输入数据,还应包括程序执行这些数据后预期的结果。 • 其他 著名计算机软件科学家E.W.Dijkstra曾断言:“程序测试只能证明错误的存在,而不能证明错误的不存在”。可以证明,除了很小的程序外,无论使用任何方法,要想做到彻底的测试,即发现程序中的所有错误,是不现实的。 常见所谓“β版”商业软件,就是软件正式发行前的测试版本。
二、算法与流程图1、算法(algorithm)P14 • 算法:解题思路(解题步骤等) 算法有表示方式: • 伪码(pseudocode) 用人类语言的形式(通常是英语)表示算法。 伪码不在计算机上执行,仅供程序员缩写程序之前构思时用(*注意伪码程序只包含执行语句,没有声明语句,后者仅仅是给编译器提供的信息) • 流程图(flow chart) 用图示方式表示算法 编程依据(便于检查) 编程时用 使用流程图的优点:不易出错/便于编程/便于别人阅读和检查程序。 • 通常编程的技术路线是: 用伪码和自顶向下、逐步求精的方法来制定算法,然后再编写相应的C语言程序。 复杂程序处理部分宜用流程图表示程序处理的过程。
二、算法与流程图1、算法(algorithm)示例:根据学生成绩输出评定结果。二、算法与流程图1、算法(algorithm)示例:根据学生成绩输出评定结果。 伪码语句示例之一 if student’s grades greater than or equal to 60 display ” Passed” else display ”Failed” C语言源程序段示例 if (grade>=60) printf(“Passed!”); else printf(“Failed”); 伪码语句示例之二 如果学生成绩大于或等于60 显示”及格” 否则 显示 ”不及格”
二、算法与流程图2、传统流程图 P20 • 特点: 直观形象; 使用流线。 • 缺点: 占面积大,使用流线任意转移,易出现“乱麻”现象,造成编程与阅读程序困难。
二、算法与流程图3、N-S结构化流程图 P27 77年美学者I.Nassi和B.Scheiderman提出。 • 特点 ①取消流线 ②不允许流程任意转移,只能从上而下顺序执行 ③规定三种基本结构的流程图单元,由这些基本结构象搭积木似的组成各种算法(结构化设计)。 • 优点 算法清晰,流程不会无规律乱转移。
二、算法与流程图4、三种基本结构P24- • 通常情况下,程序中的语句是以所编写的顺序一句接一句地执行,这种执行方法称为“顺序执行”。许多C语句能够让程序员指定不按编写顺序执行下一条执行语句,这种执行方法称为“控制转移”。 • Bohm和Jacopini的研究证实,所有的程序都能够只用三种控制结构编写,即 ◆顺序结构 ◆选择结构(selection structure) ◆循环结构(repetition structure) 顺序结构是C语言的基本结构,除非指示转移,否则计算机自动以语句编写的顺序一句一句地执行C语句。 • 任何C语言程序都是由七种控制结构(顺序结构、三种选择结构和三种循环结构)构成的。
二、算法与流程图4、三种基本结构P24- • 顺序结构 A块、B块顺序执行(每块代表一个或一组操作)
二、算法与流程图4、三种基本结构P24- • 选择结构 条件p成立时执行a块(否则执行b块) • if 选择结构 条件为真时执行某个指定的操作,条件为假时跳过该操作(单路选择) • if…else选择结构 条件为真时执行某个指定的操作,为假时执行另一个指定的操作(双路选择) • switch选择结构 根据表达式的值执行众多不同操作中的某个指定的操作(多路选择)
二、算法与流程图4、三种基本结构P24- • 循环结构 分当型和直到型两类。 当型 先判断,只要条件为真就反复执行A块,为假则结束循环。 直到型 先执行A块,再判断条件是否为真,为真则继续执行循环体,为假则结束循环。 • C语言提供了三种循环结构,即while循环结构,do…while循环结构和for循环结构。
三、选择结构程序设计1、if语句P95 三种形式: • if(表达式)语句; (图5.5a) • if(表达式)语句1; else语句2; (图5.5b) • if(表达式1)语句1; (图5.6) else if(表达式2)语句2; …… else if(表达式n)语句n; else语句n+1; ◆e1?e2:e3是if … else语句在特定情况下的变体。
三、选择结构程序设计1、if语句示例 【例一】以下程序的作用是什么? main( ) { char c; printf(“Input :”); scanf(“%c”,&c); if (c>=’a’ && c<=’z’) c=c-32; else c=c; printf(“%c”,c); } /*将小写字母转换为大写字母*/
三、选择结构程序设计1、if语句示例 【例二】以下程序的执行结果是什么? main( ) { int x=2,y=-1,z=2; if (x<y) if (y<0) z=0; else z+=1; printf(“%d\n”,z); } 哦,原来是因为else 总是与靠近它的if配套… /*结果是2*/ 常见错误: if (x>0) ; if (x=2) if (1<=x<=10) y=3x+2; printf(“y>0”); y=x-1;
三、选择结构程序设计2、switch语句P104 switch 语句的一般形式: switch(e) { case c1:语句组1; casec2:语句组2; …… casecn:语句组n; default:语句组n+1; /*可缺省*/ } /*e – 表达式(整型、字符型或枚举型)*/ c1~cn 常量(整数、字符、常量表达式如3+4,不含变量或函数) default – 不是c1~cn的情况 (位置不一定在最后)。
三、选择结构程序设计2、switch语句示例 ?! main() { char s; scanf(“%c”,&s); switch(s) { case ‘A’ : printf(“85~100\n”); case ‘B’ : printf(“70~84\n”); case ‘C’ : printf(“60~69\n”); case ‘D’ : printf(“<60\n”); default : printf(“错误输入\n”); } } 结果: 60~69 <60 错误输入 输入“c”,求输出结果。
三、选择结构程序设计2、switch语句示例 main() { char s; scanf(“%c”,&s); switch(s) { case ‘A’ : printf(“85~100\n”); case ‘B’ : printf(“70~84\n”); case ‘C’ : printf(“60~69\n”); case ‘D’ : printf(“<60\n”); default : printf(“错误输入\n”); } } 运行结果: 60~69 <60 错误输入 不好意思,我属于C级! 我的成绩应该是60~69! 什么!“<60”? “错误输入”?怎么会这样?!
三、选择结构程序设计2、switch语句示例 解决方法——break语句: main() { char s; scanf(“%c”,&s); switch(s) { case ‘A’ : printf(“85~100\n”);break; case ‘B’ : printf(“70~84\n”);break; case ‘C’ : printf(“60~69\n”);break; case ‘D’ : printf(“<60\n”);break; default : printf(“错误输入\n”); } } 运行结果: 60~69 break! 这才差不多!
讨论 switch(s)语句中的s实际上并非真正的条件选择,而只是一种跳转指示(与if语句不同),表示下面应该跳转到什么位置继续执行。而各case实际上只是一个跳转处的标记。当程序跳转到某个case处时,并非只执行此case行的程序组,而是从此处开始一直向下执行各条语句,直到整个switch开关体结束(“}”)。 如果要使每个case处相当于一种if(s)else的效果,必须在其语句组最后加上break语句。 三、选择结构程序设计2、switch语句示例 解决方法——break语句: main() { char s; scanf(“%c”,&s); switch(s) { case ‘A’ : printf(“85~100\n”);break; case ‘B’ : printf(“70~84\n”);break; case ‘C’ : printf(“60~69\n”);break; case ‘D’ : printf(“<60\n”);break; default : printf(“错误输入\n”); } }
说明 1、每个case常量表达式的值必须互不相同,否则会出现互相矛盾的结果。 2、允许多个case共用一个执行语句。 三、选择结构程序设计2、switch语句示例 main() { int x=1,y=0,a=0,b=0; switch(x) { case 1 : switch(y) { case 0 : a++;break; case 1 : b++;break; } case 2 : a++;b++;break; case 3 : a++;b++; } printf(“a=%d,b=%d\n”,a,b); } 求程序运行结果。 结果:a=2,b=1。 如果x=2? 结果:a=1,b=1 如果x=3? 结果:a=1,b=1
三、循环结构程序设计 循环是在循环条件为真时计算机反复执行的一组指令(循环体)。 循环控制通常有两种方式: ◆计数控制 事先能够准确知道循环次数时用之 用专门的循环变量来计算循环的次数,循环变量的值在每次执行完循环体各语句后递增,达到预定循环次数时则终止循环,继续执行循环结构后的语句。 ◆标记控制 事先不知道准确的循环次数时用之 由专门的标记变量控制循环是否继续进行。当标记变量的值达到指定的标记值时,循环终止,继续执行循环结构后的语句。
四、循环结构程序设计 在C语言中可用以下语句构成循环: • if … goto • while • do … while • for 其中if … goto是通过编程技巧(if语句和goto语句组合)构成循环功能。而且goto语句将影响程序流程的模块化,使程序可读性变差,所以结构化程序设计主张限制goto语句的使用。 其他三种语句是C语言提供的循环结构专用语句。
四、循环结构程序设计 名词解释 无限循环 死循环 循环结构两大要素: • 循环条件p 结束循环的条件表达式 • 循环体 A 循环执行的语句或语句组 设置循环条件要特别注意确定: • 循环变量的初值 • 循环变量的终值 • 循环变量的变化规律 名词解释 空循环
四、循环结构程序设计1、if … goto语句循环结构P113 当 型 【例一】 main() { int n=0,sum=0; loop: sum+=n; ++n; if (sum<=10000) goto loop; printf(“n = %d\n”,n); } 【例二】 main() { int n=0,sum=0; loop: if (sum>=10000) goto end; sum+=n; ++n; goto loop; end: printf(“n = %d\n”,n); } 直到型
四、循环结构程序设计2、while语句循环结构P114 当 型 • 一般形式 while (条件表达式)循环体; • 用于构成当型循环:先判断后执行/条件为真继续循环,直到条件为假时结束循环。 • 【注意】条件表达式或循环体内应有改变条件使循环结束的语句,否则可能陷入“死循环”。 【例三】 main() { int n=0,sum=0; while (sum<=10000) { sum+=n; ++n; } printf(“n = %d\n”,n); }
四、循环结构程序设计3、do…while语句循环结构P108 【例四】 main() { int n=0,sum=0; do { sum+=n; ++n; } while (sum<=10000) ; printf(“n = %d\n”,n); } • 一般形式 do { 循环语句(组) } while (条件表达式); • 用于构成直到型循环:先执行后判断/条件为真继续循环,直到条件为假时结束循环。 • 【注意】条件表达式或循环体内同样应有改变条件使循环结束的语句,否则可能陷入“死循环”。 直到sum超过10000为止 直到型
四、循环结构程序设计3、do…while语句循环结构示例 【例五】从键盘输入一个整数12456,分析以下程序运行结果。 main() { int num,c; printf(“请输入一个整数:“); scanf(“%d”,&num); do { c=num%10; printf(“%d”,c); } while((num/=10)>0); printf(“\n”); } 结果: 65421 将各位数字反序显示出来 /*取得num的个位数*/ /*输出num的个位数*/ /*直到num/10为0*/
一般形式 for (表达式1;条件表达式;表达式3) 循环语句(组); 用于构成计数型当型循环:先判断后执行/条件为真继续循环,直到条件为假时结束循环。 表达式1: 整个循环中只执行1次,常用来对循环变量设置初值 条件表达式(表达式2): 其值为真(非0)时继续执行循环语句(组),否则结束循环 表达式3: 常用于循环变量值的更新(循环体的一部分每次循环语句组执行完后执行一次) 四、循环结构程序设计4、for语句循环结构P118 【例六】 求∑i =1+2+3+4…+99+100 (i=1~100) main() { int i , s=0; for (i=1;i<=100;i++) s=s+i; printf(“Sum=%d\n”,s); }
break 结束循环 在switch中退出switch结构; 在循环中结束循环。 continue 结束本次循环 循环“短路” (跳过循环体后面的语句,开始下一轮循环)。 goto 跳转 跳到循环体外指定标号处。 【注意】 goto 语句只能从循环内向外跳转 ,反之不可! main( ) { int a,y; a=10,y=0; do { a+=2;y+=a; if (y>50) break; } while (a=14); printf("a=%d,y=%d\n",a,y); } 四、循环结构程序设计5、其他循环控制结构P122 变量跟踪 分析法 变量跟踪 a y 10 0 12 12 14+2 16+12=28 14+2 16+28=44 14+2 16+44=60 结果:a=16,y=60
break 结束循环 在switch中退出switch结构; 在循环中结束循环。 continue 结束本次循环 循环“短路” (跳过循环体后面的语句,开始下一轮循环)。 goto 跳转 跳到循环体外指定标号处。 【注意】 goto 语句只能从循环内向外跳转 ,反之不可! 求以下程序段执行后x和i的值。 int i,x; for ( i=1,x=1;i<=50;i++) { if (x>=10) break; if (x%2==1) { x+=5; continue; } x-=3; } 四、循环结构程序设计5、其他循环控制结构P122 变量跟踪 i x 1 1→6 2 6→3 3 3→8 4 8→5 5 5→10 6 结果:x的值为10,i的值为6
break 结束循环 在switch中退出switch结构; 在循环中结束循环。 continue 结束本次循环 循环“短路” (跳过循环体后面的语句,开始下一轮循环)。 goto 跳转 跳到循环体外指定标号处。 【注意】 goto 语句只能从循环内向外跳转 ,反之不可! main() { int i=1; while (i<=15) if (++i%3!=2) continue; else printf("%d ",i); printf("\n"); } 四、循环结构程序设计5、其他循环控制结构P122 变量跟踪 i ++i%3 输出i 1→2 2 2 2→3 0 3→4 1 4→5 2 5 5→6 0 …… 结果:2 5 8 11 14
break 结束循环 在switch中退出switch结构; 在循环中结束循环。 continue 结束本次循环 循环“短路” (跳过循环体后面的语句,开始下一轮循环)。 goto 跳转 跳到循环体外指定标号处。 【注意】 goto 语句只能从循环内向外跳转 ,反之不可! main() { int i,k=0; for (i=1; ;i++) { k++; while (k<i*i) { k++; if (k%3==0) gotoloop; } } loop:printf("%d,%d",i,k); } 四、循环结构程序设计5、其他循环控制结构P122 结果:2 ,3
编程示例※ 题目:编写一个用户密码校验程序。用户根据提示输入密码,如果密码正确,显示“Welcome!”信息;密码不正确,除提示密码输入错误外,允许再输入密码,如果三次输入均错,显示“你是非法用户”,然后结束程序。 • 程序分析(画出传统流程图和N-S流程图) • 密码输入部分分析(画出N-S流程图) • 现场程序编写与调试 • 程序不足处说明(留给学生解决)
五、常用算法 1、枚举法(穷举法) “笨人之法”:把所有可能的情况一一测试,筛选出符合条件的各种结果进行输出。 【例一】百元买百鸡:用一百元钱买一百只鸡。已知公鸡5元/只,母鸡3元/只,小鸡1元/3只。 分析: 这是个不定方程——三元一次方程组问题(三个变量,两个方程) x+y+z=100 5x+3y+z/3=100 设公鸡为x只,母鸡为y只,小鸡为z只。
百元买百鸡问题分析 main() { int x,y,z; for (x=0;x<=100;x++) for (y=0;y<=100;y++) for (z=0;z<=100;z++) { if (x+y+z==100 && 5*x+3*y+z/3.0==100 ) printf("cocks=%d,hens=%d,chickens=%d\n",x,y,z); } } 结果:x=0,y=25,z=75 x=4,y=18,z=78 x=8,y=11,z=81 x=12,y=4,z=84 【讨论 此为“最笨”之法——要进行101×101×101= 1030301次(100多万次)运算。
百元买百鸡问题分析 main() { int x,y,z; for (x=0;x<=100;x++) for (y=0;y<=100;y++) { z=100-x-y; if (5*x+3*y+z/3.0==100 ) printf(“cocks=%d,hens=%d,chickens=%d\n",x,y,z); } } 取x<=19,y<=33 只进行20×34= 680 次运算(第1种运算的6.7%) 【讨论】 令z=100-x-y 只进行101×101= 10201 次运算(前者的1%)
【例二】雨水淋湿了算术书的一道题,8个数字只 能看清3个,第一个数字虽然看不清,但可看出不是1。编程求其余数字是什么?[ □×(□3+□)]2 = 8□□9 • 分析 设分别用A、B、C、D、E五个变量表示自左到右五个未知的数字。其中A的取值范围为2~9,其余取值范围为0~9。条件表达式即为给定算式。
【例二】雨水淋湿了算术书的一道题,8个数字只 能看清3个,第一个数字虽然看不清,但可看出不是1。编程求其余数字是什么?[ □×(□3+□)]2 = 8□□9 main() { int A,B,C,D,E; for (A=2;A<=9;A++) for (B=0;B<=9;B++) for (C=0;C<=9;C++) for (D=0;D<=9;D++) for (E=0;E<=9;E++) if (A*(B*10+3+C)*A*(B*10+3+C)==8009+D*100+E*10) printf(“%2d%2d%2d%2d%2d\n”,A,B,C,D,E); } 结果:3 2 8 6 4
【例三】求100~200之间不能被3整除也不能被7整除的数。【例三】求100~200之间不能被3整除也不能被7整除的数。 • 分析:求某区间内符合某一要求的数,可用一个变量“穷举”。所以可用一个独立变量x,取值范围100~200。 for (x=100;x<=200;x++) if (x%3!=0&&x%7!=0) printf(“x=%d\n”,x); 如果是求指定条件的奇数呢? x=101;x<=200;x=x+2 如果是求指定条件的偶数呢? x=100;x<=200;x=x+2
五、常用算法 2、归纳法(递推法) “智人之法”: 通过分析归纳,找出从变量旧值出发求新值的规律。 【例一】 编程求∑i =1+2+3+4…+99+100 (i=0~100) 分析 i=0 S0= 0 (初值) i=1 S1= 0+1=S0+1 i=2 S2=1+2=S1+2 i=3 S3=1+2+3=S2+3 i=4 S4=1+2+3+4=S3+4 … … … i=n Sn=1+ 2+3+4+…+n=Sn-1+n
如果是 ∑i =1+1/2+1/3+…+1/n 呢? 【例一】 编程求∑i =1+2+3+4…+n ( n ≤100) 程序: main() { int i ,n, s=0; printf("n="); scanf("%d",&n); for ( i=1;i<=n;i++) s=s+i; printf("Sum=%d\n",s); } 运行结果: n=100 Sum=5050
算法类型小结:累加型 【累加型】类型诸如 □+□+□+□+……+□+□ 求其前n项之和的编程题。 累加型算法 若设i为循环变量,s为前n项累加之和,则程序的基本结构为: s=0; for( i=1 ;i<=n ;i++ ) s=s+□;
【例二】编程求1-1/2+1/3-1/4+1/5- … +1/99-1/100 错在哪里? 法1:从变化规律分析…… • 分母为奇数时,相加 • 分母为偶数时,相减 程序: main() { int i; float s=0; for (i=1;i<=100;i++) if (i%2) s=s+1/i; else s=s-1/i; printf("Sum=%f\n",s); } 运行结果:Sum=1.000000
【例二】编程求1-1/2+1/3-1/4+1/5- … +1/99-1/100 法2:这是个累加型算法的编程题…… 错在哪里? (如何检查程序错误?) 程序: #include <math.h> main(); { int i ; float s=0; for (i=1;i<=100;i++) s=s+pow (-1, i+1) / i ; printf("Sum=%f\n",s); } 累加型算法 程序基本结构为: s=0; for( i=1;i<=n;i++ ) s=s+□; 程序: #include <math.h> main() { int i ,k=1; float s=0; for (i=1;i<=100;i++) { s=s+k / i ; k = -k ; } printf("Sum=%f\n",s); } 运行结果:Sum=0.688172 运行结果:Sum=1.000000
【例三】 编程求n! (n由键盘输入) 分析 i=0 S0=1=S0 (初值) i=1 S1= 0×1=S0×1 i=2 S2=1×2=S1×2 i=3 S3=1×2×3=S2×3 i=4 S4=1×2×3×4=S3×4 … … … i=n Sn=1× 2×3×4×…×n=Sn-1×n