绝对零基础的C语言科班作业⑥循环结构(上)(for,while,do-while,break,continue)

一.循环

①(代码)从1加到N的和

任务描述

有数学王子之称的著名数学家高斯,和阿基米德、牛顿、欧拉并列为世界四大数学家,

一生成就极为丰硕。

高斯10岁的时候,数学课上老师布置了一道题数学题:从1一直加到100等于多少。全班只有高斯算出了答案5050,而且很快。起初老师并不相信高斯算出了正确答案,高斯就解释他是如何找到答案的,他发现:1+100=101,2+99=101,3+98=101,……,49+52=101,50+51=101,一共有50对和为101的数目,所以答案是50×101=5050。

现在,老师给你布置一道编程题,输出从1加到N的和。

输入格式:

一个整数N(1=<N<=100000)。

输出格式:

一个整数,从1加到N的和。

任务分析

我们都知道,从1到n的自然数是一个等差数列,首项是1,公差也是1,共n项。所以根据等差数列的求和公式S,很容易得到从1加到 n的和为 。

解法1:根据等差数列公式直接求解

#include<stdio.h>
int main()
{
    int n, s;
    scanf("%d", &n); //输入整数n 
    s = n * (n + 1) / 2;    //公式直接计算从1加到n的和s 
    printf("%d", s); //输出结果 
    return 0;
}

代码测试与分析

在Dev-C++中执行:输入:1 输出:1 (测试边界数据,可能的最小值)输入:10 输出:55输入:100 输出:5050输入:1000 输出:500500输入:0 输出:0 (尽管任务中说明不可能有0,这里只为测试用)

以上代码大家一定都能理解,现在我们来讨论另一种解法,就是我们首先设置一个和变量s=0,用来存储最终的和,初值为0。然后设置计数器变量i,我们让i的值从1到n变化,每一次都把i累加到变量s里(s=s+i)。最后,变量s的值就是所求,输出即可。这就像初始时s是一个空的篮子,我们让计数器i从1数到n,每数一次就扔i个小球到篮子里。最后,篮子里的小球数量就是答案。每次扔i个小球到篮子里,这是重复操作,计数器i从1数到n是重复的次数。这里的重复操作就是循环的思想。

我们可以用下面的算法来描述从1加到n的求解过程:

(1)输入变量n,定义和变量s=0(初值),计数器变量i=1(初值);

(2)如果i<=n成立就向下执行,如果不成立转到(6);

(3)s=s+i;(4)i=i+1;(5)转到(2);(6)输出s。以上就是求解从1加到n的和算法的形式化描述,其中的步骤(3)和(4)就是被重复执行的部分,我们称为循环体。计数器变量i称为循环变量,i<=n称为循环条件。为了实现以上算法中的循环结构,C语言为我们设计了while语句、do-while语句和for语句三种循环结构,专门处理这类问题。

相关知识 while循环(当型循环)

C语言中用while语句用来实现“当型”循环结构,它的一般形式如下:while(循环条件表达式){ 循环体语句;}

(1)首先求解循环条件表达式的值,若值为真,则执行循环体,否则结束循环。

(2)每一次的循环体执行完成后,自动跳转到循环开始(while)处,再次求解循环条件表达式的值,如果成立就开始下一次循环,如此往复。

(3)循环体只能是一个语句,所以如果有多个语句,则应该用大括号将其括起来使之成为一个复合语句;如果循环体只是一个语句,大括号也可以省略。

解法2:while语句

#include<stdio.h>
int main()
{
    int n, s, i;      //定义变量
    scanf("%d", &n); //输入整数n                   ---算法步骤(1)
    s = 0; i = 1;       //和变量s,计数器变量i赋初值 ---算法步骤(1)
    while (i <= n)  //满足循环条件就进入循环       ---算法步骤(2)
    {   
        s = s + i;      //将变量i的值累加到和变量s中 ---算法步骤(3)
        i = i + 1;      //计数器i向后计数             ---算法步骤(4)
    }               //                             ---算法步骤(5)
    printf("%d", s); //输出结果                     ---算法步骤(6)
    return 0;
}

代码测试与分析

在Dev-C++中执行:输入:1 输出:1 (测试边界数据,可能的最小值)输入:10 输出:55输入:100 输出:5050输入:1000 输出:500500输入:0 输出:0 (尽管任务中说明不可能有0,这里只为测试用)

结合while语句的语法规则,结合上文中的算法,这个代码是不是很好理解呢?这就是循环结构的代码,让循环变量i从1变到n,循环体执行n次的代码框架,我们可以称为计次循环(循环次数固定可数),一定要牢记: i=1; //循环变量赋初值 while(i<=n){ //进入循环的条件 循环体; i=i+1; //循环变量的增量 }

相关知识 do-while循环(直到型循环)

C语言还为我们提供了do-while语句用来实现“直到型”循环结构,它的一般形式如下:

do

循环体语句;

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

(1)在此结构中do相当于一个标号,标志循环结构开始。

(2)首先无条件地执行一次循环体,然后求解循环条件表达式的值。若表达式的值为真,则跳转到do处再次执行循环体;若循环条件表达式的值为假则结束循环。

(3)如果循环体只有一条语句,花括号可以省略。

(4)do-while结构整体上是一条语句,所以while的括号后应加上分号。

以上两种循环比较,可以理解为:while循环是在入口处判断条件,do-while循环是在出口处判断条件。

解法3:do-while语句

#include<stdio.h>
int main()
{
    int n, s, i;
    scanf("%d", &n); //输入整数n 
    s = 0; i = 1;       //和变量s赋初值0,计数器变量i赋初值1  
    do 
    {             //循环开始的标记 
        s = s + i;      //将变量i的值累加到和变量s中 
        i = i + 1;      //计数器i向后计数 
    } while (i <= n);   //满足循环条件就再次进入循环 
    printf("%d", s); //输出结果 
    return 0;
}

代码测试与分析

在Dev-C++中执行:输入:1 输出:1 (测试边界数据,可能的最小值)输入:10 输出:55输入:100 输出:5050输入:1000 输出:500500输入:0 输出:1 (尽管任务中说明不可能有0,这里只为测试用)

解法3的代码同样可以实现任务要求的功能,程序的原理和执行过程和解法2是完全一致的,只不过do-while循环是先执行1次循环体,再判断循环条件,对于输入的n>=1的时候,两种解法的输出结果都是一致的,都可以实现题目要求。小白弟弟,请思考:如果我们输入的是小于1的整数(例如输入0)送给变量n,情况会怎么样呢?以上测试数据中已经显示出如果输入0,执行结果是:解法1输出0;解法2输出1。为什么会这样呢?因为解法1的while循环是在循环开始处判断(1<0)不成立,循环立即结束,循环体被执行0次,最后输出的s的值为0;解法2的do-while循环是在循环出口处判断循环条件,循环体无论如何要执行1次,才能到出口处,执行的这1次循环体已经给变量s累加了1,最后输出的s的值为1。任务中的描述说输入的n是大于等于1的,所以解法2在此情况下才是正确的,如果输入的n可能为0,解法2就不正确了。

相关知识 for循环

除了while语句和do-while语句,C语言还为我们提供了另外的一个使用更为广泛的循环语句——for语句。for语句的一般形式为:for(表达式1;循环条件表达式2;表达式3){ 循环体语句;}(1)首先求解表达式1(该表达式只在这一步骤处被求解一次)。(2)求循环条件解表达式2,若为真则执行循环体,否则结束for语句。(3)循环体执行结束后,求解表达式3,并转向步骤(2)。(4)循环体如果只是一条语句,花括号可以省略。

解法4:for语句

#include<stdio.h>
int main()
{
    int n, s, i;
    scanf("%d", &n);    //输入整数n 
    s = 0;               //和变量s赋初值0
    for (i = 1;i <= n;i++) 
    { //典型的计次循环,i从1开始到n结束,每次加1
        s = s + i;         //循环体 
    }
    printf("%d", s);    //输出结果 
    return 0;
}

代码测试与分析

在Dev-C++中执行:输入:1 输出:1 (测试边界数据,可能的最小值)输入:10 输出:55输入:100 输出:5050输入:1000 输出:500500输入:0 输出:0 (尽管任务中说明不可能有0,这里只为测试用)

从以上代码和测试结果可以看出,for循环可以方便地表达计次循环,for(i=1;i<=n;i++)清楚地说明了循环变量i从1开始,到n结束,每次加1。

从执行逻辑上看,先执行i=1,然后进入循环“i<=n→s=s+i→i++”,直到i<=n不成立。这跟while语句的逻辑是相同的,可见while循环和for循环是可以互相转换的:

while循环和for循环可以方便地互相转换,我们在设计程序时可以根据问题实际需要选择一种使用。小白弟弟同样也要牢记,让循环变量i从1变到n,循环体执行n次的计次循环(循环次数固定可数)代码框架: for(i=1;i<=n;i++){ 循环体; }

相关知识 穷举法

有一类问题在进行归纳推理时,如果需要逐个考察某类事件的所有可能情况,即将所有可能情况一一列举,这种方法叫做穷举法。穷举法将问题的所有可能的答案一一列举,然后根据条件判断此答案是否合适,合适就保留,不合适就丢弃。例如,输出从1加到N的和,可以将1到N之间的所有整数一一列举,无需判断,每个数都累加到变量S中,最后S中的数就是所求;例如,输出自然数N的阶乘,需要将1到N之间的所有整数一一列举,无需判断,每个数都累乘到变量F中,最后F中的数就是所求;例如,输出1到N之间的奇数,可以将1到N之间的所有整数一一列举(穷举),符合条件(奇数)输出,不符合条件就略过;例如,输出1到N之间的素数,需要将1到N之间的所有整数一一列举,是素数的输出,不是素数的略过;例如,输出N的所有约数,需要将1到N之间的所有整数一一列举,如果是N的约数就输出,不是则略过。

相关知识 程序测试方法

我们在测试一个程序的时候,除了使用题目中给出的输入样例做为测试数据以外,还要尽可能地增加多组输入测试数据,尤其是一些边界数据(可能的最大值、最小值),或者特殊数据(0值、特殊意义的值等)。

②(代码)素数判断

任务描述

一个大于1的自然数p,除了1和本身p以外,不能被其他自然数整除,称p为素数(又称质数,prime number),称p为合数。已知素数有无限多个,但是到目前为止,人们未找到一个公式可求出所有质数。

2016年1月,发现世界上迄今为止最大的质数,长达2233万位,如果用普通字号将它打印出来长度将超过65公里。

素数从小到大排列,有2、3、5、7、11、13、17、19、23、29、31、37、41、43、47、53、59、61、67、71、73、79、83、89、97······

输入一个大于1的正整数N,输出其是否为素数,如果是输出YES,否则输出NO。

任务分析

素数的定义是只有1和它本身两个约数的自然数。从定义出发,我们可以做如下统计:如果一个大于1的自然数p的约数个数是2个,那么它就是素数。而统计p的约数个数,我们可以穷举从1到p的所有自然数,这就是计次循环,可以用for语句实现。

解法1:穷举(for语句)

#include<stdio.h>
int main()
{
    int p, i, s;
    scanf("%d", &p);       //输入整数n 
    s = 0;                  //统计约数个数的变量s,赋初值0 
    for (i = 1;i <= p;i++)    //穷举循环变量i从1到p 
    {
        if (p % i == 0)
        {
            s++;          //如果i是p的约数,则计数 
        }
    }
    if (s == 2)              //如果s==2输出YES,否则输出NO 
    {
        printf("YES");
    }
    else
    {
        printf("NO");
    }
    return 0;
}

代码测试与分析

在Dev-C++中执行或者在PTA中执行自定义测试:输入:2 输出:YES (边界值,可能的最小值)输入:53 输出:YES输入:15 输出:NO

代码中通过for循环穷举从1到p的自然数i,循环体内如果i是p的约数,就计数。循环结束后,如果计数结果s的值等于2,就说明p是素数,输出YES,否则输出NO。因为1和p是当然是p的约数,所以,我们也可以统计从2到p-1的范围内,p的约数是否为0个,来判断p是否为素数。

解法2:穷举(for语句)

#include<stdio.h>
int main()
{
    int p, i, s;
    scanf("%d", &p);       //输入整数n 
    s = 0;                  //统计约数个数的变量s,赋初值0 
    for (i = 2;i < p;i++)    //穷举循环变量i从2到p-1 
    {
        if (p % i == 0) s++;   //如果i是p的约数,则计数 
    }
    if (s == 0)              //如果s==0输出YES,否则输出NO 
    {
        printf("YES");
    }
    else
    {
        printf("NO");
    }
    return 0;
}

解法2的代码可以提交正确,和解法1比较,对于同样的输入数据p,循环少执行2次。其实,还可以更少。我们可以知道,一个自然数p,如果在p以下没有约数,则在以上不可能有约数。因为不可能有2个都大于p的数乘积为p。所以,想知道p有没有约数,只要穷举2到p之间的数就可以了。

解法3:穷举(for语句)

#include<stdio.h>
#include<math.h>
int main()
{
    int p, i, s;
    scanf("%d", &p);       //输入整数n
    s = 0;                  //统计约数个数的变量s,赋初值0
    for (i = 2;i <= sqrt(p);i++) //穷举循环变量i从2到根号p
    {
        if (p % i == 0)
        {
            s++;      //如果i是p的约数,则计数
        }
    }
    if (s == 0)                 //如果s==0输出YES,否则输出NO
    {
        printf("YES");
    }
    else
    {
        printf("NO");
    }
    return 0;
}

解法3代码的逻辑是统计从2到之间p的约数个数s,如果s==0,说明p就是素数。解法3代码的循环次数已经大大减少了,以输入的p=10000为例,解法1循环10000次,解法2循环9998次,而解法3穷举的是从2到100,循环99次。可见,解法3比解法1和解法2效率更高,执行时间更短。对于判断一个数是否为素数,我们可以采用反证法,就是它在2到p的区间内只要有一个约数,就说明它已经不是素数了,没必要统计所有的约数个数。

解法4:穷举(for语句)

#include<stdio.h>
#include<math.h>
int main()
{
    int p, i, f;
    scanf("%d", &p);       //输入整数n 
    f = 1;                  //标志变量f,赋初值1 
    for (i = 2;f == 1 && i <= sqrt(p);i++)   //穷举i从2到根号p,进入循环时需要f==1
    {
        if (p % i == 0)
        {
            f = 0;  //如果i是约数则置f为0(改变标志,下次循环进不来)
        }
    }
    if (f == 1)             //如果f==1输出YES,否则输出NO 
    {
        printf("YES");
    }
    else
    {
        printf("NO");
    }
    return 0;
}

解法4中应用了一个特别的技术:设置标志变量f。f的初值是1,如果从2到p之间找到1个约数,就立刻将f的值改变成0,而一旦f的值变为0,则下一次循环就进不来了。因为循环条件是f==1&&i<=sqrt(p),只要f的值不是1,条件就不成立,循环就结束了。最后,如果f==1成立,就说明在区间内没有找到约数,p肯定是素数,否则就不是素数。小白弟弟请思考:假设输入的p是10000,那么解法4的循环次数是多少次呢?对了,是1次。因为当第1次循环时,i的值是2,此时i正是p的约数,f会被赋值成0,下一次循环条件不成立,循环结束了。所以,解法4是最优的。因为它只找第一个素数,而不是找所有的素数。

相关知识 设置标志变量

在一些编程任务中,通常需要识别某种状态,例如上例任务中识别自然数p是否有约数,设置标志变量是一个好办法,它能准确记录状态的变化,通过条件控制程序进程,从而简化程序设计。

③(代码)斐波那契数列

任务描述:

Fibonacci(斐波那契)数列指的是这样一个数列:1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,17711,28657,46368... ...这个数列前2项是1,从第3项开始,每一项都等于前两项之和。

Fibonacci数列的递推公式为:

编程读入整数n(1<=n<=40),输出Fibonacci数列的前n项。

#include<stdio.h>
int main()
{
    int n, i;
    int f1, f2, f3;
    f1 = 1;
    f2 = 1;
    scanf("%d", &n);
    if (n == 1)
    {
        printf("1");
    }
    if (n == 2)
    {
        printf("1,1");
    }
    if (n > 2)
    {
        printf("1,1,");
        for (i = 3;i <= n;i++)
        {
            f3 = f1 + f2;
            f1 = f2;
            f2 = f3;
            if (i < n)
            {
                printf("%d,", f3);
            }
            else
            {
                printf("%d", f3);
            }
        }
    }
    return 0;
}

二.循环控制(break和continue)

①(代码)从A加到B的和

任务描述

编程输入正整数A和B(A<=B),输出从A加到B的和,即从A到B的连续自然数的和。

输入格式:

两个正整数A和B(A<=B)。

输出格式:

一个整数,从A加到B的和。

你注意到了吗,前面关卡中我们设计的循环程序,都是从循环条件表达式处,判定是否进入循环,如果条件不成立才结束循环。C语言还为我们提供了一个能从循环体内部跳出循环的break语句,就像跳出switch语句一样,当执行到break语句时,跳出循环结构,结束循环。

相关知识 break语句

break语句用于从循环体内跳出循环结构,一般形式是:break;(1)在循环体内,当程序执行到break语句时,会立即跳出循环结构,结束循环。(2)break语句通常出现在某个if语句的分支中,以实现有条件的结束循环。(3)break语句只能用于switch结构内部或循环结构内部。

相关知识 continue语句

C语言还提供了另一个用于控制循环结构的语句:continue语句,用于结束当次循环,直接跳到循环开始处,开始下次循环,一般形式是:continue;(1)在循环体内,当执行到continue语句时,会立即结束本次循环(跳过循环体中后面的部分不执行),接着跳转到循环开始处,执行下一次循环。(2)continue语句通常出现在某个if语句的分支中。(3)continue语句只能用于循环结构的内部。

解法1:穷举(while循环)

#include<stdio.h>
int main()
{
    int a, b, s, i;
    scanf("%d%d", &a, &b); //输入整数a,b 
    s = 0;                 //和变量s赋初值0
    i = a;                 //循环变量i赋初值a 
    while (i <= b)     //循环判断条件(穷举从a到b的自然数)   
    {
        s = s + i;           //循环体将i累加到和变量中 
        i++;             //循环变量i自加1 
    }
    printf("%d", s);    //输出结果 
    return 0;
}

代码测试与分析

除任务题目中给出的测试数据外,在Dev-C++或PTA中执行:输入:1 1

输出:1 (可能的最小值组合)输入:1 100 输出:5050输入:1 99 输出:4950

以上代码,你一定理解吧,这是一个非常普通的计次循环穷举问题,循环变量从a开始到b结束,每次加1。有的时候,我们也可以不用在循环入口处关心循环什么时候结束,而是在循环体内通过条件判断来决定什么时候跳出循环。

解法2:使用break语句

#include<stdio.h>
int main()
{
    int a, b, s, i;
    scanf("%d%d", &a, &b); //输入整数a,b 
    s = 0;                 //和变量s赋初值0
    i = a;                 //循环变量i赋初值a 
    while (1)           //循环条件永为真(好像死循环) 
    {
        s = s + i;           //循环体将i累加到和变量中 
        i++;             //循环变量i自加1
        if (i > b)
        {
            break;   //如果i>b跳出循环,实现从a到b的穷举 
        }
    }
    printf("%d", s);    //输出结果 
    return 0;
}

代码分析

代码中通过while(1){ }结构构造一个形式上的死循环,就是循环条件永远为真,永远也不会结束的循环。在循环体内部,通过if(i>b) break;语句,实现当i<=b时继续下一次循环并累加,当i>b时跳出循环。

②(代码)重做素数判断

任务描述

一个大于1的自然数p,除了1和本身p以外,不能被其他自然数整除,称p为素数(又称质数,prime number),称p为合数。已知素数有无限多个,但是到目前为止,人们未找到一个公式可求出所有质数。

2016年1月,发现世界上迄今为止最大的质数,长达2233万位,如果用普通字号将它打印出来长度将超过65公里。

素数从小到大排列,有2、3、5、7、11、13、17、19、23、29、31、37、41、43、47、53、59、61、67、71、73、79、83、89、97······

输入一个大于1的正整数N,输出其是否为素数,如果是输出YES,否则输出NO。

任务分析

任务要求输入一个大于1的正整数p,若p是素数输出YES,否则输出NO。判断p是否为素数,可以通过穷举从2到的自然数,如果存在p的约数则p就不是素数,为了识别是否有约数,设置标志变量。

解法1:使用break语句

#include<stdio.h>
#include<math.h>
int main()
{
    int p, i, f;
    scanf("%d", &p);          //输入整数n 
    f = 1;                     //标志变量f,赋初值1 
    for (i = 2;i <= sqrt(p);i++)  //穷举循环变量i从2到 
    {
        if (p % i == 0)
        {
            f = 0;              //如果i是约数则置f为0 
            break;            //跳出循环 
        }
    }
    if (f == 1)                 //如果f==1输出YES,否则输出NO 
    {
        printf("YES");
    }
    else
    {
        printf("NO");
    }
    return 0;
}

代码分析

代码中的循环条件处,还原回最初的样子,只是表达了i从2穷举到,比较清晰。循环体内,通过break跳出循环也在情理之中,很好理解。下面我们一起来看看应用continue语句的代码。

解法2:使用continue语句

#include<stdio.h>
#include<math.h>
int main() 
{
    int p, i, f;
    scanf("%d", &p);          //输入整数n 
    f = 1;                     //标志变量f,赋初值1 
    for (i = 2;i <= sqrt(p);i++)  //穷举循环变量i从2到根号p 
    {
        if (p % i != 0)       //如果i不是约数
        {
            continue;        //就直接执行下次循环(跳到i++再判断循环条件)
        }
        else
        {                //能执行到这i肯定是约数 
            f = 0;              //置标志变量f为0 
            break;            //跳出循环 
        }
    }
    if (f == 1)                 //如果f==1输出YES,否则输出NO 
    {
        printf("YES");
    }
    else
    {
        printf("NO");
    }
    return 0;
}

代码分析

continue语句的功能就是直接跳至下一次循环,在解法2的代码循环体中,执行逻辑是:如果i不是p的约数,直接continue后进入下一次循环,否则标志变量改值,并跳出循环

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GR鲸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值