4.4 循环结构

循环结构是在一定条件下,反复执行某段程序的控制结构,被反复执行的语句序列称为循环体。在Java语言中循环结构是由循环语句来实现的。Java语言中的循环语句共有三种:while语句、do-while语句和for语句。

4.4.1 while语句

while语句是循环语句,也是条件判断语句。while语句的一般语法结构如下:

     while(条件表达式)
     {
       循环体
     }

循环体可以是单个语句,也可以是复合语句。while语句的执行过程是先判断条件表达式的值,若为真,则执行循环体,循环体执行完之后,再转到条件表达式重新计算表达式的值并判断条件表达式值的真假;直到当计算出的条件表达式的值为假时,才跳过循环体执行while语句后面的语句,循环终止。

while语句的循环执行过程如图4.5所示。

图4.5 while语句的循环执行过程

例4.5】 计算Fibonacci(斐波那契)序列的前16项。Fibonacci序列的通项公式为

程序运行结果:

     0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610

该程序的第8~14行是一个while循环,第10行是每次输出两个数,第11和12两行分别是用于计算Fibonacci序列中的下一个数。

例4.6】 从键盘上输入一个数,判断该数是否是Fibonacci序列中的数。

程序运行结果:

     请输入一个正整数:234↙
     234不是Fibonacci数

该程序的第12~13行是从键盘上输入一个数;第14行是while循环的头,第16~18行是循环体,用于计算Fibonacci数列的递推公式;第20、21两行根据计算的结果判断其是否为Fibonacci数列中的数,然后输出相应的结果。

例4.7】 利用hasNextXXX()和nextXXX()方法的配合使用来完成键盘输入。用户在键盘上输入若干个数,每输入一个数需按Enter键或Tab键或空格键确认,最后在键盘上输入一个非数字字符串结束整个输入操作过程,然后计算这些数的和。hasNextXXX()和nextXXX()方法的功能见3.6节。

程序运行结果:

     请输入多个数,每输入一个数后按Enter或Tab或空格键确认:
     最后输入一个非数字结束输入操作
     3 ↙
     4.8 ↙
     5 ↙
     5.6 ↙
     w ↙
     共输入了4个数,其和为:18.4

程序中的第11行是用指定的流创建一个Scanner类的对象reader,第12行判断只要有数据输入且是double型,则进入循环内,第14行是从键盘上读取一个double型数值存入x中。该程序运行时,用户在键盘上每次输入一个数值后都需要按Enter或Tab键或空格键进行确认,最后输入一个非数字字符结束输入操作,因为当输入一个非数字字符并按Enter键后,reader.hasNextDouble()的值为flase。

说明当要求输入的数据是较长的数据类型(如double型)时,但实际输入的数据是较短的数据类型(如int或float)时,则系统会自动的强制转换成较长的数据类型的数据(如double型)。

4.4.2 do-while语句

do-while语句的一般语法结构如下。do-while循环语句的流程如图4.6所示。

图4.6 do-while循环语句的流程

     do
     {
         循环体
     }
     while(条件表达式);

do-while语句的使用与while语句很类似,不同的是它不像while语句是先计算条件表达式的值,而是无条件地先执行一遍循环体,再来判断条件表达式的值,若表达式的值为真,则再执行循环体,否则跳出do-while循环,执行下面的语句。可见,do-while语句的特点是它的循环体至少被执行一次。

注意与while循环语句的一个主要区别是do-while循环语句在结尾处加了一个分号“;”。

例4.8】 从键盘上输入一个正整数n,然后计算1+2+…+n的结果并输出。

程序运行结果:

该程序的第9~12行是利用do-while循环从键盘上输入数据,直到输入的数大于0为止;第13、14两行是利用while循环求和;第15行输出计算结果。

例4.9】 用辗转相除法求两个整数的最大公约数。

设有不全为0的整数a和b,它们的最大公约数记为gcd(a,b),即同时能整除a和b的公因数中的最大者。按照欧几里得(Euclid)的辗转相除算法,gcd(a,b)具有如下性质:

①gcd (a, b)=gcd (b, a)。

②gcd (a, b)=gcd (—a, b)。

③gcd(a,0)=|a|。

④gcd (a, b)=gcd (b, a%b), 0≤a%b<b。

本例程序中反复运用性质④,最终可使得第二个参数a%b等于0,则第一个参数就是所求的最大公约数。程序如下:

程序运行结果如下:

     请输入第一个数a=12↙
     请输入第二个数b=18↙
     gcd(12,18)=6

该程序在第18~22行的do-while循环中,利用辗转相除法来求两个数的最大公约数,第23行是将最大公约数输出。

例4.10】 已知s=n!,其中n为正整数,从键盘上任意输入一个大于1的整数m,求满足s<m时的最大s及此时的n,并输出s和n的值。

程序运行结果为:

     请输入大于1的整数m:100↙
     s=24  n=4

该程序的第9~12行的do-while语句是要求所输入的数必须为大于1的整数,否则重复循环,直到输入大于1的整数为止;第13~17行是利用while循环求满足条件n!<m的阶乘;当退出该循环时,则s和n并不满足本题的条件,所以第18行在输出时,必须修正为s/(n—1)和n—2,这才是满足题意要求的结果。

4.4.3 for语句

for语句是Java语言三个循环语句中功能较强,使用较广泛的一个。

for循环语句的基本使用格式如下。其执行流程如图4.7所示。

图4.7 for循环结构的流程

     for(表达式1;条件表达式;表达式2)
     {
         循环体
     }

其中,“表达式1”是用作初始化的表达式,完成初始化循环变量和其他变量的工作;“条件表达式”的返回值为逻辑型量,用来判断循环是否继续;“表达式2”是循环后的操作表达式,用来修改循环变量,改变循环条件。三个表达式之间用分号隔开。

for语句的执行过程:首先计算“表达式1”,完成必要的初始化工作;再判断条件表达式的值,若为假,则退出循环,若为真,则执行循环体,执行完循环体后再返回“表达式2”,计算并修改循环条件,这样一轮循环就结束了。第二轮循环从计算并判断条件表达式开始,若表达式的值仍为真,则继续循环,否则,跳出整个for语句执行下面的句子。

说明for语句的三个表达式都可以为空,但是,若条件表达式也为空,则表示当前循环是一个无限循环,需要在循环体中书写另外的跳转语句来终止循环。

例4.11】 求1~10的累加和。

本例演示了for语句的两种使用方法。

方法一:循环变量i以递增方式从1变化到n,循环体语句执行n=10次,循环执行完后输出结果s。

方法二:i以递减方式变化。由于要写出累加的算式,i及加号“+”应写在循环体中,而n个数只需要写n—1个加号,所以循环语句只需执行n—1=9次就够了,此时设计i从n变化到2,循环执行完后再写最后一个i值及最后一次运算的结果(s+i)。

本例采用两种方法分别运算,程序如下:

该程序的执行结果如下:

     Sum=1+…+10=55
     Sum=10+9+8+7+6+5+4+3+2+1=55

该程序是用两个for循环语句分别完成从1~10累加求和的。第7、8行是第一个for循环,该循环的循环体只有第8行一条语句,所以不用花括号将其括上,该循环是从1~10进行累加求和;第12~16行是第二个for循环,该循环是从10~2进行累加求和;最后的数1是在第17行的输出语句中加上的。

4.4.4 多重循环

如果循环语句的循环体内又有循环语句,则称多重循环,也称循环嵌套。常用的有二重循环和三重循环。在实现手段上即可以是相同循环语句嵌套,也可以是两个不同的循环语句构成嵌套结构。下面举例说明。

例4.12】 求100以内的素数,并输出。

素数是指除1和自身外,不能被其他整数整除的数。显然最小的素数是2,其余偶数均不是素数。对于一个奇数k,使用3~的每个整数j去除k,如果找到一个整数j能除尽k,则k不是素数;而只有测试完3~中的所有整数j都不能除尽k,才能确定k是素数。程序如下:

程序运行结果如下:

在本程序第15行中的Math.sqrt(k)方法返回k的平方根值。第12~24行定义的do-while外层循环,用于遍历3~100的奇数;第15、16行定义的内层while循环,用于判别k是否是素数,当找到一个3~的整数j能除尽k,则k不是素数,退出内循环,此时j<Math.sqrt(k)。内层循环结束后,如果第17行的j>Math.sqrt(k)条件成立,说明没有一个j能除尽k,则k是素数。外层do-while循环逐个测试100以内的奇数,循环初值k=3,每次递增值为2,这样可以减少循环次数,缩短运行时间,提高程序效率。