第四周:循环控制

4-1 for循环

for循环像一个计数循环:设定一个计数器,初始化它,然后在计数器到达峰值之前,重复执行循环体,而每执行一轮循环,计数器以一定步进进行调整,比如加1或者减1

for循环语法:

for (循环变量初始化; 循环条件判断; 循环变量赋值){

循环体

}

整个for循环按以下步骤执行:

  1. 初始化循环变量
  2. 判断循环条件,成立执行循环体否则结束循环。
  3. 循环变量赋值,程序回到第2步
int i;
for (i = 1; i <= 5; i++){
printf("%d,", i);
}
printf("\ni=%d",i);
/*执行结果:
1,2,3,4,5,
i=6
*/

Tips for loops

  • 如果有固定次数,用for
  • 如果必须执行一次,用do_while
  • 其他情况用while

4-2 循环控制

要求程序读取用户输入的一个整数n,并告诉用户这个数是否是素数。素数:只能被1和自己整除的数,不包括1,比如2,3,5,7,11,17,19...

题目分析:

程序如何判断一个数能被另一个数整除?

将两个数做取余运算符,如果结果为0就表示前者能被后者整除。比如4%2=0 ,就表示4能被2整除。

如何判断一个数只能被1和自身整数?

直接一点的方法就是用1到n之间的数去当做取余数,n作为被取余数,两者做取余运算。1到n之间只要有一个数能被整数,那么这个数就不是素数,否则这个数就是一个素数。当一个数为n时超过n的中间值的那个数都不可能对n做整除了,因此我们可以省掉一半的计算,用1至n/2之间的数和n做取余运算即可。

  1. 声明变量isPrimeNumber并赋初始值 1 1=素数,0=非素数
  2. 程序接受用户输入整数放入number中
  3. 如果number等于1,则将isPrimeNumber赋值为0
  4. 初始化循环变量i的值为2
  5. 判断i是否小于等于number/2

    5.1 小于等于用number和i做取余运算,若取余结果为0则isPrimeNumber赋值为0并且程序跳转到第6步,

    否则程序跳转至第5.3步

    5.2 否则程序跳转到第6步

    5.3 i自增1,程序回到第4步
  6. 判断isPrimeNumber如果是1输出number为素数否则输出number不是素数。

代码实现:

#include <stdio.h>

void main(int argc, char *argv[]) {
int number = 0;
int isPrimeNumber = 1;
int middle = 0;
printf("请输入一个整数:");
scanf("%d", &number); if(number == 1){
isPrimeNumber = 0;
} else {
int i = 2;
middle = number/2;
for(; i <= middle; i++){
if(number % i == 0){
isPrimeNumber = 0;
break;
}
}
} if(isPrimeNumber == 1){
printf("数字%d是一个素数", number);
} else {
printf("数字%d不是一个素数", number);
}
}

测试样例:

请输入一个整数:7
数字7是一个素数
--------------------------------

我们如果想知道100以内的素数改怎么办呢?显然我们需要判断2到100的所有整数,如果是素数我们就输出。

让我们一起来把程序改造下。

程序实现:

#include <stdio.h>

void main(int argc, char *argv[]) {
int j = 2;
int i = 2;
int middle = 0;
int isPrimeNumber = 1; printf("100以内的素数:\n");
for(; j < 100; j++) {
middle = j/2;
isPrimeNumber = 1;
i = 2;
for(; i <= middle; i++){
if(j % i == 0){
isPrimeNumber = 0;
break;
}
}
if(isPrimeNumber == 1){
printf("%d ", j);
}
}
}

运行样例:

100以内的素数:
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
--------------------------------

break vs continue

tips:(vs versus 对比)

  • break:跳出循环
  • continue : 跳过这一轮循环剩下的语句并进入下一轮循环

逻辑运算

  • 逻辑运算是对逻辑量进行的运算,结果只有0或1
  • 逻辑量是关系运算或逻辑运算的结果

各运算符优先级(图)

短路

  • 逻辑运算时自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
  • a6 && b1
  • a==6 && b+=1
  • 对于&&,左边是false就不做右边了
  • 对于||,左边是true就不做右边了

** 例子:凑硬币 **

  • 如何用1角、2角和5角硬币凑出2元?

    分析:
  • 用m1,m2,m5分别代表1角、2角和5角
  • 既然是凑出那么m1、m2、m5都至少要出现一次
  • xm1 + ym2 + z*m5 = 20 本质上就是一个一元三次方程。
  • 我们知道x的取值范围[1,13] y的取值范围[1,7],z的取值范围[1,3]
  • 因此我们用一个x,y,z的嵌套循环就能求解了,并且和循环的嵌套的顺序无关。
  • x、y、z的下限哦们知道是1,上限目前是根据具体的金额2元算出来的,那么如果我们要算的是3元、5元或者其他金额我们的程序还需要修改。显然我们不希望这样,那有没有办法不修改程序还能适应各种金额呢?
  • 其实不难,假如总数是zs x上限 xs = zs - m2 - m5,就是总数减去其他两个数各取1的值。对于ys,zs同理。
  • 当x,y,z解的交集恰好等于20就是方程的有效解了。

代码实现:

#include <stdio.h>

void main(int argc, char *argv[]) {
void combination()
{
int zs = 20;
int one = 1;
int two = 2;
int five = 5; for (int x = 1; x <= (zs - two - five) / one; x++)
{
for (int y = 1; y <= (zs - one - five) / two; y++)
{
for (int z = 1; z <= (zs - one - two) / five; z++)
{
if (x * one + y * two + z * five == zs)
{
printf("可以用%d个一角加%d个两角加%d个五角得到2元\n", x, y, z);
}
}
}
}
}

测试样例:

可以用1个一角加2个两角加3个五角得到2元
可以用1个一角加7个两角加1个五角得到2元
可以用2个一角加4个两角加2个五角得到2元
可以用3个一角加1个两角加3个五角得到2元
可以用3个一角加6个两角加1个五角得到2元
可以用4个一角加3个两角加2个五角得到2元
可以用5个一角加5个两角加1个五角得到2元
可以用6个一角加2个两角加2个五角得到2元
可以用7个一角加4个两角加1个五角得到2元
可以用8个一角加1个两角加2个五角得到2元
可以用9个一角加3个两角加1个五角得到2元
可以用11个一角加2个两角加1个五角得到2元
可以用13个一角加1个两角加1个五角得到2元 --------------------------------

如果我们只想得到一个结果怎么做呢?有读者可能会想到在最里面的for循环也就是z所在的for循环加一个break,实际上我们会发现,即使加上了break,运行的结果还是一样,原因就是break只会跳出离他最近的那一个循环,因此我们需要一点小的技巧来处理。

跳出多重循环:

#include <stdio.h>

void main(int argc, char *argv[]) {
{
int zs = 20;
int one = 1;
int two = 2;
int five = 5;
int exist = 0; for (int x = 1; x <= (zs - two - five) / one; x++)
{
for (int y = 1; y <= (zs - one - five) / two; y++)
{
for (int z = 1; z <= (zs - one - two) / five; z++)
{
if (x * one + y * two + z * five == zs)
{
printf("可以用%d个一角加%d个两角加%d个五角得到2元\n", x, y, z);
exist=1;
break;
}
} if (exist) {
break;
}
} if (exist) {
break;
} }
}

测试样例:

可以用1个一角加2个两角加3个五角得到2元

--------------------------------

tips:我们还可以使用c语言的goto关键词来实现循环的退出,但是goto容易破坏掉程序的顺序结构,因此不推荐使用。

求两个数的最大公约数

辗转相除法

  1. 如果b等于0,计算结束,a就是最大公约数
  2. 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
  3. 回到第一步

代码实现:

#include <stdio.h>

void main(int argc, char *argv[]) {
{
int a, b;
int t;
a = 12;
b = 18; while (b != 0)
{
t = a % b;
a = b;
b = t;
}
printf("gcd=%d\n", a);
}

测试样例:

gcd=6

--------------------------------

正序分解整数

  • 输入一个非负整数,正序输出它的每一位数字
  • 输入:13425
  • 输出:1 3 4 2 5

题目分析:

  • 要正序输出就要得到数字的最高位,然后把这个数字除以10再得到最高位,一直到循环最低位就是正序分解的结果了。
  • 我们知道一个数字13425/10000 就能得到最高位,再用13425%10000就能得到3425取掉最高位的数字。因此num/10000就是最高位, num%10000 就是余下的数字。
  • 但是10000要根据数字的大小来定,所以第一步应该先计算数字有多少位来决定是除10还是除10000
  • 计算整数的位数我们整除10直到结果等于0,除了多少次加1就是几位数,10的位数减一的次方就是我们第一次分解需要的除数

    代码实现:
#include <stdio.h>

void main(int argc, char *argv[])
{
int x;
printf("请输入一个正整数:");
scanf("%d",&x);
int mask = 1;
int t = x;
while (t >= 10)
{
t /= 10;
mask *= 10;
} do
{
int d = x / mask;
printf("%d", d); if (mask >= 10)
{
printf(" ");
}
x %= mask;
mask /= 10; } while (mask > 0);
printf("\n");
}

测试样例:

请输入一个正整数:13425
1 3 4 2 5
-------------------------------- 请输入一个正整数:500000
5 0 0 0 0 0 --------------------------------

4-3 课后习题

1 .题目内容: 题目内容:

我们认为 2 是第一个素数,3 是第二个素数,5 是第三个素数,依次类推。

现在,给定两个整数 n 和 m,0<n<=m<=200,你的程序要计算第 n 个素数到第 m 个素

数之间所有的素数的和,包括第 n 个素数和第 m 个素数。

输入格式:

两个整数,第一个表示 n,第二个表示 m。

输出格式:

一个整数,表示第 n 个素数到第 m 个素数之间所有的素数的和,包括第 n 个素数和第 m

个素数。

输入样例:

2 4

输出样例:

15

题目分析:

  • 要计算第n个素数, 第 m个素数,以及他们之间素数的和
  • 怎么计算第n个素数呢?用index来记录是第几个素数,然后统计从2开始的素数,每统计一个index自增1,当我们统计到第n个素数时,将和记录在sum中,继续统计下一个直到第m个素数,累加值sum就是我们要求的和。
  • 我们需要一个数素数的循环f1,循环遍历i从2开始,在循环内部判断这个数是否是素数,执行完i步进1
  • 判断是素数的判断中需要添加index是否到n和是否到m的判断对sum做累加操作
  • 我们最多要数到第200个素数,但是我们目前还不知道第200个素数的值是多少,因此我们不妨把第一个循环的值设置大一点。比如10000,先假设这些数中足够200个素数了。

程序实现:

#include "sumPrime.h"

void sumPrime()
{
int j = 2;
int i = 2;
int middle = 0;
int isPrimeNumber = 1;
int index = 0;
int sum = 0;
int n, m;
int isCalcRange = 0;//是否计算一个范围内素数的和,如果m==n 只需计算第n 个素数的值即可,因此当且仅当m>n 时此变量值为1 printf("请输入n和m以空格隔开 0<n<=m<200:\n");
scanf("%d %d", &n, &m);
isCalcRange = m > n;
for (; j < 10000; j++)
{
middle = j / 2;
isPrimeNumber = 1;
i = 2;
for (; i <= middle; i++)
{
if (j % i == 0)
{
isPrimeNumber = 0;
break;
}
} if (isPrimeNumber == 1)
{
index++; if (index >= n)
{
if (index == m)
{
sum += j;
break;
}
else
{
sum += j;
}
} }
}
printf("%d\n",sum);
}

测试样例:

请输入n和m以空格隔开 0<n<=m<200:
2 4
15 -------------------------------- 请输入n和m以空格隔开 0<n<=m<200:
200 200
1223 -------------------------------- 请输入n和m以空格隔开 0<n<=m<200:
1 200
111587 -------------------------------- 请输入n和m以空格隔开 0<n<=m<200:
0 0
5736396 --------------------------------

tips:思考下当我们输入两个0时计算的结果是什么?我们改如何改进我们的程序才不会出现这样的情况?

2.题目内容:

你的程序要读入一个整数,范围是[-100000,100000]。然后,用汉语拼音将这个整数

的每一位输出出来。

如输入 1234,则输出:

yi er san si

注意,每个字的拼音之间有一个空格,但是最后的字后面没有空格。当遇到负数时,在输出

的开头加上“fu”,如-2341 输出为:

fu er san si yi

输入格式:

一个整数,范围是[-100000,100000]。

输出格式:

表示这个整数的每一位数字的汉语拼音,每一位数字的拼音之间以空格分隔,末尾没有空格。

输入样例:

-30

输出样例:

fu san ling

题目分析:

  • 前面我们做过整数的正序拆分,只需要把拆分后的数字对应成拼音即可。
  • 这里出现了负数,我们用isNegative记录这个数是否是负数,然后对负数做取负的运算,之后的计算和正数已知,最后再通过判断isNegative进行符号的输出即可。

    程序实现:
#include <stdio.h>
#include "getDigitPinYin.h" void main(int argc, char *argv[])
{
int x;
int isNegative = 0;
printf("请输入一个整数[-100000,100000]:");
scanf("%d", &x); if (x < 0)
{
isNegative = 1;
x = -x;
printf("fu ");
} int mask = 1;
int t = x;
while (t >= 10)
{
t /= 10;
mask *= 10;
} do
{
int d = x / mask; switch (d)
{
case 0:
printf("ling");
break;
case 1:
printf("yi");
break;
case 2:
printf("er");
break;
case 3:
printf("san");
break;
case 4:
printf("si");
break;
case 5:
printf("wu");
break;
case 6:
printf("liu");
break;
case 7:
printf("qi");
break;
case 8:
printf("ba");
break;
case 9:
printf("jiu");
break;
default:
printf("unown");
break;
} if (mask >= 10)
{
printf(" ");
}
x %= mask;
mask /= 10; } while (mask > 0);
printf("\n");
}

测试样例:

请输入一个整数[-100000,100000]:-30
fu san ling -------------------------------- 请输入一个整数[-100000,100000]:12345
yi er san si wu --------------------------------

4-4 讨论题

标题:利用循环变量来判断素数不好吗?

内容:

课程中提到有种“聪明”的做法,可以不设 isPrime,直接利用循环出口处循环变量和终点值

的关系来判断循环是否 break 了。你觉得这种做法好吗?

我认为这种做法不可取,有至少以下两点原因:

  1. 程序不易于阅读,如果通过变量isPrime一眼能看出程序要表达的意思,如果用出口循环变量判断需要思维转个弯才能理解。
  2. 不易扩展维护,如果我们的程序不判断到number/2,而是number-1,那么这个出口循环变量的判断也需要做更改。

程序设计入门-C语言基础知识-翁恺-第四周:循环控制-详细笔记(四)的更多相关文章

  1. 程序设计入门-C语言基础知识-翁恺-期中测试

    一.试题 程序设计入门—C 语言期中测评 试题下载地址: http://nos.netease.com/edu-lesson-pdfsrc/217E194E46A6595A3F554380337490 ...

  2. 程序设计入门-C语言基础知识-翁恺-第一周:简单的计算程序-详细笔记(一)

    目录 第一周:简单的计算程序 1.1 第一个程序 Hello World! 1.2 变量 1.3 计算 1.4 编程作业及课后讨论 第一周:简单的计算程序 1.1 第一个程序 Hello World! ...

  3. 程序设计入门-C语言基础知识-翁恺-第七周:指针与字符串-详细笔记(七)

    目录 第七周:指针与字符串 7.1 指针初步 7.2 字符类型 7.3 字符串 7.3 课后练习 第七周:指针与字符串 7.1 指针初步 sizeof 是一个运算符,给出某个类型或变量在内存中所占据的 ...

  4. 程序设计入门-C语言基础知识-翁恺-第五周:函数-详细笔记(五)

    目录 第五周:函数 5.1 函数 5-2 使用函数 5.3 课后习题 第五周:函数 5.1 函数 什么是函数? 函数是一块代码,接受零个或多个参数,做一件事情,并返回零个或一个值. 函数声明语法 返回 ...

  5. 程序设计入门-C语言基础知识-翁恺-第二周:简单的计算程序-详细笔记(二)

    目录 第二周:判断 2.1 比较 2.2 判断 2.3 课后习题 第二周:判断 2.1 比较 简单的判断语句: if(条件成立){ //执行代码 } 条件 计算两个值之间的关系,所以叫做关系运算 关系 ...

  6. 程序设计入门-C语言基础知识-翁恺-第六周:数组-详细笔记(六)

    目录 第六章:数组 6-1 数组 6-2 数组计算 6.3 课后习题 第六章:数组 6-1 数组 题目:让用户输入一组整数以-1结束输入,算出这组数的平均值,并且输出大于平均值的数. 我们需要记录用户 ...

  7. 程序设计入门-C语言基础知识-翁恺-第三周:循环-详细笔记(三)

    目录 第三周:循环 3.1 循环 3.2 循环计算 3.3 课后习题 3.4 讨论题(不需要掌握) 第三周:循环 3.1 循环 while循环 语法: while(条件表达式){ //循环体语句 } ...

  8. Golang 入门系列(三)Go语言基础知识汇总

    前面已经了 Go 环境的配置和初学Go时,容易遇到的坑,大家可以请查看前面的文章 https://www.cnblogs.com/zhangweizhong/category/1275863.html ...

  9. 程序设计入门——C语言 习题汇总

    <img width="108" height="40" alt="浙江大学" src="http://imgsize.ph ...

随机推荐

  1. Css初步认识

    css 美化页面 cascading style sheet 层叠样式表 css语法:  选择器{ css属性名:属性值;css属性名:属性值;} css引入方式:  方式一:内联样式表   通过标签 ...

  2. UVA 12063 Zeros and Ones(三维dp)

    题意:给你n.k,问你有多少个n为二进制的数(无前导零)的0与1一样多,且是k的倍数 题解:对于每个k都计算一次dp,dp[i][j][kk][l]表示i位有j个1模k等于kk且第一位为l(0/1) ...

  3. 利用shell编程,部署项目到服务器

    现在在前后端分离的开发形式中,每次前端将VUE项目打包之后,需要后端程序员部署到服务器上.这过程为何没有用git,因为每次vue打包后的文件都不相同与前一次打包,git为何的话,会包含过大迭代版本,同 ...

  4. Nginx 与 tomcat 部署网站

    http://www.blogjava.net/libin2722/articles/355631.html http://congpeixue.iteye.com/blog/255011 keepa ...

  5. Android -- ContentObserver 内容观察者

    1. 实现原理图 2. 示例代码 (暂时有个问题,短信观察者 收到一条短信时 onchange方法会执行两次, 解决方法为:每次监听到变化的时候就去取最新短信的id,跟上次取的比较,如果一样的就不做处 ...

  6. C# 一些常用的字符串扩展方法

    以下可能是常用的.net扩展方法,记录下 EString.cs文件 /// <summary> /// 扩展字符串类 /// </summary> public static ...

  7. 51nod 1187 寻找分数

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  8. linux 用 grep 查找单个或多个字符串(关键字)

    1.单个 cat /tmp/php.log | grep "成功" 所有的成功都会被查询出来. 2.多个,并列查询 cat /tmp/php.log | grep "推荐 ...

  9. JS的scrollIntoView

    scrollIntoView(alignWithTop)  滚动浏览器窗口或容器元素,以便在当前视窗的可见范围看见当前元素.如果alignWithTop为true,或者省略它,窗口会尽可能滚动到自身顶 ...

  10. [Vue]vee-validate的使用——自定义校验规则及校验message

    1.安装vee-validate npm install vee-validate --save 2.main.js里引用vee-validate插件 import Vue from 'vue' im ...