(注:课后习题有一定难度,我们开始加速了,请拉好扶手(≖ᴗ≖)✧。)
循环结构
我们先来看一道题:
例4.1
输出数字1~10
看起来很简单:
1 | cout<<"1 2 3 4 5 6 7 8 9 10"; |
那么,如果稍微修改一下这道题:
例4.2
输出数字1~1000
这个问题确实是有必要考虑的,因为计算机是要处理大量数据的,编写程序的手段也要能够接受大量的数据。
是不是要键盘敲1000个数呢?请看另一道题:
例4.3
输入整数n,m,输出[n,n+m]中的整数
我们发现已经束手无策,只使用if将写到天荒地老(
这样无疑是很麻烦的,编程序本来就是为了节省时间(体力),有没有更好的方法呢?能不能让计算机自动地进行重复劳动呢?
C/C++语言提供了循环解决这一问题(毕竟并非所有语言都有循环语句的…)。
while循环
while循环顾名思义,“当”条件成立的时候反复执行语句,“当”条件不成立的时候结束执行语句。while循环的语法如下:
1 | while(条件) |
语句块在上一章中已经学过,在后面的循环结构中都可以使用。
这里的条件就是循环中的语句的执行条件,如果条件不成立就结束循环。
无法停止的循环是没有意义的,我们设计循环结构的程序时需要注意的第一个问题就是什么时候让它停止。
对于之前的例4.2,我们就可以用while循环来实现:
1 | int i=1; |
我们来分析一下这段代码:
第一行:定义一个整数i初值1
第二行:while语句开始循环,因为条件i<=1000成立,故进入循环语句
第三、四行:执行语句块:先输出i,再让i赋值为i+1
其中要强调几个概念:
- 变量i 控制着这个循环的运行,称为循环变量
- i<=1000 这个表达式控制了循环的执行/退出时机,称为循环条件
- i++ 即i=i+1 为循环变量在循环中的改变量,称为增量(也有称步长的)
这些是设计循环结构的程序时需要思考设计的点,也是循环程序的要点所在。
这个程序的流程图如下,便于大家理解:
这样,我们便实现了计算机自动进行重复劳动。
for循环
for取英语中“对每一个”的意思(for each),即让计算机取一个循环变量遍历一个区间。从本质上说只是while循环的一种变形,它提供了初值、循环条件、增量的集中管理。然而C/C++语言中的for语句十分灵活,for循环又非常常用(基本能不用while就不用…),所以熟练使用for语句的使用也是掌握C/C++语言的一个指标。
for语句语法如下:
1 | for(初值;循环条件;增量) |
上面的例4.2,用for循环就可以写成:
1 | int i; |
for循环语句也可以将循环变量定义在语句内:
1 | for(int i=1;i<=1000;i++) |
要注意的是,在语句内定义的循环变量在循环外将不再有效,即无法在循环外使用,此特性被称为变量的作用域,此概念将在函数一节详细说明。
第二行:
for语句中:
初值为i=1,即把i赋值为1后进入此for循环
循环条件i<=1000,即当i<=1000时循环,i<=1000不成立时结束循环。
增量i++,即每次执行完要进入下一次循环时,i=i+1;
上面的这段代码,也实现了上面流程图的功能,与之前while循环的程序时等价的。
这里可能会有疑问:while循环和for循环有什么区别?什么时候使用哪种循环呢?
while循环和for循环的功能基本上是一致的,通常一个for循环的语句都可以用一个对应等价的while循环代替。提供for循环的一个主要原因就是方便,它能够覆盖大多数我们需要用到循环的地方,理解起来也方便。而对于while循环,需要考虑的地方则较多。所以一般情况,能使用for循环解决的时候就优先for循环。
例如上例4.3:
1 | int n,m; |
可以观察到两者写法的区别。
循环的嵌套(1)
我们之前学过if语句的嵌套,而循环(和其他大部分语句一样)也是可以嵌套的。
1 | for(int i=1;i<=3;i++) |
尝试一下上面的代码,看看输出的是什么,试着理解循环的嵌套。
输出:
i=1 j=1
i=1 j=2
i=1 j=3
i=2 j=1
i=2 j=2
i=2 j=3
i=3 j=1
i=3 j=2
i=3 j=3
上面的代码画成流程图如下:

好,现在请你准备好纸笔,自己执行一下这个流程图,并与输出对照,自己试试分析上面的三行代码。
分析一下这个程序:
第一行:一个以i为循环变量的for循环,范围为1~3
第二行:第一个for循环所要执行的语句,我们发现还是一个for循环,第二个for以j为循环变量,范围为1~3
第三行:开始执行第二个for循环,分别从j=1~3print出了i和j的值,此时的i的for循环只执行了1次,
因此i的值一直为1
之后j的循环结束了,i的循环中此时i=2再次进入j循环,j又从1~3走一遍,print出i=2,j=1~3
同理i循环中此时i=3再次进入j循环。print出i=3,j=1~3
总共:i循环执行了1次,i循环中的语句——j循环执行了3次;j循环执行的每一次,j循环中的语句——printf语句执行了3次。即printf语句总共执行了3*3=9次。
这里,对于这样的两个循环的嵌套,我们称为二重循环,同理,三个循环的嵌套,称为三重循环,以此类推。
对于两个有嵌套关系的循环,如上例,称i所在的循环为外层循环;j所在的循环称为内层循环。
虽然,任意的变量名都是可以接受的,但由于在程序设计中,循环结构使用极广,循环又离不开循环变量,我们约定:一般循环变量自外到内命名为i , j ,k, l(四重循环及以上极其少见,具体原因稍后会提到)。
循环的嵌套(2)
循环的嵌套给程序编写带来了更多可能性,但也使需要掌握的细节复杂起来。
一
请比较一下两段代码:
1 | //1 |
它们都是由三个循环组成的代码,请问它们分别是几重循环?
答:1是三重循环,2是二重循环。
原因是2的j和k是并列关系,不是从属关系。
而此时,1的程序运行10*10*10次,而2为10*(10+10)次。是的,就是离散数学中的乘法原理和加法原理。
二
在之前的编程过程中,编写出的程序似乎总能立刻得出结果。
现在请自己尝试一下这个程序:
1 | int a=0; |
看看你的计算机花了多少时间?
不同的计算机运算能力不同,但我们日常能接触到的计算机的算力不会相差太大。我这里测下来是用了4.267秒跑完这段程序,你的计算机应该也不会瞬间就出现结果。
那么这个程序做了什么事呢。按照之前对循环嵌套的分析,发现这里最内层的a++一句执行了2000*1000*1000=2*10^9次。 于是我们发现,计算机运算很快,但也是有极限的,而且对我们来说,这个极限很低。
事实上,一般我们要求一个程序要在一秒内得出正确结果才算合格。试想,你在使用一个应用程序的时候点击一个选项,执行一个操作都要等个五秒钟,那体验一定是极差的。
然而,从上面的实验看出,估算计算机一秒的运算次数是一亿次,所以在编写循环嵌套的程序时发现明明输入了却迟迟不出结果,就要考虑你的程序的运算是否太过庞大。
例题
循环的第一课内容就讲到这里,但这是初学编程遇到的第一个难点,这里安排几道例题讲解便于熟悉循环结构程序的设计。
例4.4
对于等差数列An=2n,键盘输入正整数n,输出数列前n项和
首先想到的是直接上求和公式算,这一想法是绝对值得肯定的,因为它一次就能得到答案,非常快,应该是对于此题最好的解法。不过既然讲到循环,这里用循环解决这一问题。
我们先构造一个i=1~n的循环,于是在循环过程中Ai的值就为2*i,再设置一个累加器s=0,每次加上Ai的值就可以实现求和了。
C++:
1 | int n,i,s=0; |
C:
1 | int n,i,s=0; |
例4.5
键盘输入一个正整数k,按规律输出k阶三角形
如k=4时,输出
*
**
***
****
此题是第一课题2的升级版。
观察图形发现,k阶的三角形有k行,第i行由i个*组成。
因此我们构造一个双重循环,外层i表示第i行(从1到k),内层j表示第i行的第j个*(从1到i),最后在每一个i中的j循环结束时换行即可。
C++:
1 | int i,j,k; |
C:
1 | int i,j,k; |
作业:
思考:
1、什么是循环,它和分支有什么区别?
2、什么是循环变量,为什么要引入循环变量?
3、什么是循环的嵌套,如何计算循环中的语句的执行次数?
习题:
1、输入正整数n,m,输出n行m列的矩形*方阵
2、输入正整数n,输出斐波那契数列第n项
提示:斐波那契数列
$ F_1=F_2=1 $
$ F_n=F_{n-1}+F_{n-2} (n\geq3) $
3、输入正整数n,判断是否是质数,输出True或False
4、输入若干个学生成绩,以-1为结束标志,输出其中的平均值,最大值和最小值
例:
输入:2 4 6 8 10 -1
输出:6.000000 10.000000 2.000000
*5、输入正整数n,按规律输出n阶的字母菱形图案
当n=5时
A
ABA
ABCBA
ABCDCBA
ABCDEDCBA
ABCDCBA
ABCBA
ABA
A
提示:在include了stdlib.h之后即可使用abs(x)求一个整数的绝对值,如:
1 | int a=-1; |
输出
1