【C语言必学知识点一】分支与循环——习题演练七

导言

大家好,很高兴又和大家见面啦!!!在上一篇的末尾,博主有分享过今天的习题,大家都有下去尝试着解答吗?如果没有解答的话没关系,在这一篇章,博主将带领大家一起思考并解题,如果有解答的话那就更好了,可以在这一篇的思路分享中看看自己的想法跟博主的想法有没有什么异同点,能不能得到新的解题思路。下面我们来看一下今天要介绍的题目:

打印闰年(1000-2000)
打印素数(1-100)
数9的个数(1~100)

话不多说,咱们就开始进行今天的习题解析吧!

打印闰年(1000-2000)

闰年大家都不陌生了,这里我就简单的介绍一下吧。
全年有366天的则为闰年,我们正常的一年只有365天,多出来的一天是多在2月里,闰年的2月有29天。
介绍完闰年那我们又该如何判定闰年呢?

闰年的判定

闰年的判定有两个规则:

普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年;
世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年

这两个规则是正常年份的判别方法。
这里有朋友就会问了,如果现在是一个很大的年份如15600年,这个满不满足上述的条件呢?
对于这个问题,博主想分享给大家的是针对与这种数值很大的年份的判定规则:

能被3200整除并且还能被172800整除的才是闰年

这个判定规则咱们了解就好,在这一题的解题中不需要。
现在我们也知道如何判定闰年了,接下来我们就可以开始进行题目解析了;

题目解析

根据闰年的这两个判定规则,我们如果要编写代码来实现的话肯定需要用到分支语句,并且分支语句的判定条件就是这两个规则的内容;
题目还需要我们将1000-2000年之间的闰年全部打印出来,这里是在重复打印这个动作,所以肯定需要用到循环语句,循环的判定条件就是不超过2000;
有了思路,我们就可以开始编写代码来实现了;

代码实现

按照上述思路,我们的代码应该需要完成三个功能:

功能一:生成1000-2000的年份;
功能二:对各年份进行判定是否为闰年;
功能三:打印闰年;

接下来,我们将一一实现这些功能;

功能一——生成年份

我们要生成1000-2000的年份,这里和我们之前遇到的习题打印奇数也好,打印3的倍数也好,都是同一类题,所以我们只需要用循环语句就能实现:

	//功能一——生成年份(1000-2000)
	for (year = 1000; year <= 2000; year++)

下面我们继续实现功能二;

功能二——判断闰年

闰年的判定是需要借助分支语句的,现在我们需要将闰年的判定方法从文字转化成代码,下面我们来一句一句的转化:

普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年;

这一种情况我们可以得到的信息是年份是4的倍数并且不是100的倍数,我们判断一个数是否为另一个数的倍数可以通过什么方式呢?
有朋友反应的很快——整除。在C语言中,我们是如何表示整除的呢?
没错,就是通过取模操作符%。所以这一句转化成代码,我们就可以写成:

(0 == year % 4) && (0 != year % 100)

逻辑与左边的部分是在判断年份能不能被4整除,逻辑与右边的部分是在判定年份能不能被100整除,前面能整除,后面不能整除,则该年份为闰年;

世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年;

有了前面的经验,那这一句转化为C语言就简单了,我们只需要对上面的代码简单修改,就是这一句的代码了:

0 == year % 400

那判断条件我们都有了,这两个条件又应该如何衔接呢?
从这个两个判断条件我们可以看到,它们是在判断不同情况下的年份,第一种是非整百的年份,第二种是整百的年份,所以它们是两种情形,我们只需要在判断年份的时候满足其中一条就可以了,也就是说,这两个代码,要么是第一条的情况满足,要么是第二条的情况满足。所以我们可以通过逻辑或来将它们衔接起来,功能二的代码如下:

		//功能二——判断闰年
		if ((0 == year % 400) || ((0 == year % 4) && (0 != year % 100)))

这个功能肯定不是说我们只判断最后一年,肯定是每生成一年,我们就要判断一次,也就是说,这个功能应该是在以功能一为前提的条件下进行的,所以它的完整代码如下:

	//功能一——生成年份(1000-2000)
	for (year = 1000; year <= 2000; year++)
	{
		//功能二——判断闰年
		if ((0 == year % 400) || ((0 == year % 4) && (0 != year % 100)))
	}

现在我们只剩最后一个功能需要实现了;

功能三——打印闰年

咱们又看得到打印了,这个就不需要多解释了,我们只需要借助printf函数就能实现:

			//功能三——打印闰年
			printf("%d  ", year);

这个功能咱们要注意了,它的打印对象是闰年,我们经过前面的判定得到的闰年,需要通过这个功能打印出来,所以这个功能应该是在以功能二为前提才能实现,也就是说,它应该是功能二的内部功能,所以它的完整代码如下:

	//功能一——生成年份(1000-2000)
	for (year = 1000; year <= 2000; year++)
	{
		//功能二——判断闰年
		if ((0 == year % 400) || ((0 == year % 4) && (0 != year % 100)))
		{
			//功能三——打印闰年
			printf("%d  ", year);
		}
	}

到这里,我们的代码基本上就已经编写完了,我们来测试一下代码能否实现这题的要求:
打印闰年
可以看到,很好的实现了题目的要求,接下来附上完整代码:

//打印1000年到2000年之间的闰年
int main()
{
	int year = 0;
	//功能一——生成年份(1000-2000)
	for (year = 1000; year <= 2000; year++)
	{
		//功能二——判断闰年
		if ((0 == year % 400) || ((0 == year % 4) && (0 != year % 100)))
		{
			//功能三——打印闰年
			printf("%d  ", year);
		}
	}
	return 0;
}

这一题咱们就先介绍到这里,我们接下来开始介绍下一题;

打印素数(1-100)

这一题可能会有朋友对素数这个内容有点遗忘了,所以我们先来介绍一下素数的基本知识点;

什么是素数?

质数又称​​素数​​。一个大于1的​​自然数​​,除了1和它自身外,不能被其他自然数​​整除​​的数叫做质数;否则称为​​合数​​(规定1既不是质数也不是​​合数​​)。
看到这个概念,各位是不是马上就回忆起来了,既然素数就是质数,那它的判定方法又是什么呢?

素数的判定方法?

这里我们要介绍的判定方法是最常见的判定方法——试除法。

试除法

所谓的试除法就是指,任意一个数i如果在2~(i-1)的范围内都不能被整除,那说明i为素数。
说的再简单点就是将这个数i与1-i之间除了1和它本身的所有的数都进行除法,如果从2一直除到(i-1)都没能被整除,也就是余数都不为0,则说明这个数就是素数。
知道了方法,那我们就可以开始用代码来实现了;

代码实现

这个代码的实现我们有需要实现哪些功能呢?

  • 第一个,我们需要先有了100~200之间的目标数才能进行后续工作;
  • 第二个,它需要有2~(i-1)之间的数,所以我们肯定需要生成这些数;
  • 第三个,它需要将i和这些数进行除法来判断是否能被整除,所以我们肯定需要能够对它们进行判断的功能;
  • 第四个,在判断完之后确定了它是素数,我们还需要将其打印出来,所以我们还有能够打印;
    下面我们就来一一实现这些功能;

功能一——生成目标数

生成1~100之间的数我们遇到很多题目都需要这个要求了,这里我还是再简单的提一下,我们可以通过循环语句来完成这个功能:

	int i = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i++)

我们接着来看第二个功能;

功能二——生成范围

在有了目标数i之后,我们就可以继续利用循环来生成判断范围了,代码如下:

	int i = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i++)
	{
		//功能二——生成范围
		for (int n = 2; n < i; n++)

现在我们就成功的生成了2~(i-1)之间的所有数,下面我们就需要进行判断了;

功能三——判断素数

我们判断素数肯定是通过分支语句进行判断的,判断的条件就是,数i与范围内的数能不能整除,转化为代码如下所示:

			//功能三——判断素数
			if (0 == i % n)

这里的判断我肯定不是跟某一个数进行判断,是要跟范围内的所有数都要进行判断,所以功能二应该是在功能一的前提下进行的,代码如下:

	int i = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i++)
	{
		//功能二——生成范围
		for (int n = 2; n < i; n++)
		{
			//功能三——判断素数
			if (0 == i % n)
		}
	}

在判断完了,我们就要开始打印了;

功能四——打印素数

现在问题来了,我们要打印的是素数,我们需要在每判断一次就打印一次吗?
显然是不需要的,这里我们要打印的肯定是在所有的判断介绍后才需要打印,所以,我们的打印应该是在跳出判断范围后才能进行;
题目的要求是将1-100之间的素数全部打印出来,所以我们在打印完一个素数后要继续对下一个数进行判断并打印下一个素数,代码如下:

	int i = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i++)
	{
		//功能二——生成范围
		for (int n = 2; n < i; n++)
		{
			//功能三——判断素数
			if (0 == i % n)
		}
		//功能四——打印素数
		printf("%-2d  ", i);
	}

这个代码就完成了吗?
显然没有,现在这里还存在几个问题,我们先看一下直接运行会是什么效果,然后再针对出现的问题进行完善:
素数错误打印
从结果中我们可以看到,此时的代码并未进行正常判断进行筛选,为什么会这样呢?因为在判断完,我们并未进行任何操作,而且我们在打印时也并未对出来的值进行进一步筛选,那我们应该如何修正呢?

代码修正

我们首先要明确我们判断的目的是什么?
在if判断中我们是需要通过i与n取模是否为0来判断它是否有除1和本身以外的能被整除的数,所以如果此时判断为0 的话,那就说明它有其他能被整除的数,这个时候就不需要继续往后验证了,我们已经找到了一个值,也就是此时我们直接跳出循环就行,代码如下:

			//功能三——判断素数
			if (0 == i % n)
			{
				break;
			}

在跳出循环后我们还要在打印前判断它是否为素数,这时判断的标标准是什么呢?
没错就是跳出循环时n的值。
如果此时n与i相等,那就说明在这个范围内没有一个能被整除,此时的i就是素数,代码如下:

			//功能三——判断素数
			if (0 == i % n)
			{
				break;
			}
		}
		//功能四——打印素数
		if (n == i)
		{
			printf("%-2d  ", i);
		}

我们来运行测试一下看看:
错误打印2
可以看到,此时还是出错了,错误在n未定义,为什么会这样呢?
这里是因为局部变量的作用域在对应代码块内,其生命周期也是在对应代码块内
换句话说就是,这里的局部变量n在功能二的for循环语句中生成,出了for循环就自动销毁,这里也是初学者常犯的错误。我们应该如何修改呢?
此时我们只需要加长它的生命周期就可以了,这里我们可以通过将n的定义放在main函数的代码块内就行,代码如下:

//打印素数
int main()
{
	int i = 0;
	int n = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i++)
	{
		//功能二——生成范围
		for ( n = 2; n < i; n++)
		{
			//功能三——判断素数
			if (0 == i % n)
			{
				break;
			}
		}
		//功能四——打印素数
		if (n == i)
		{
			printf("%-2d  ", i);
		}	
	}
	return 0;
}

此时我们再来运行看看结果如何:
打印素数
可以看到此时以及能正常打印了,但是还是有一点小问题,1未被打印,之所以未被打印是因为当i为1时,n为2,循环直接跳出来了,此时n也不等于i,这里我们可以对代码进行打印条件的修改,将i为1的情况也给加上就解决了,如图所示:
打印素数2
现在咱们的编码就没问题了,我们也就完成了这一题的要求;
接下来大家跟我一起来思考一下,这一题还能不能继续优化呢?
答案肯定是可以的,接下来我们就一起来探讨一下如何优化代码;

代码优化

代码的优化我们可以从两个方面进行优化,一个是目标,一个是范围。下面我来一一介绍;

目标优化

先思考一个问题,偶数可能是素数吗?
答案是不可能,因为只要是偶数就能被2整除,也就是说偶数除了1和它本身外,还有至少一个2能被它整除,既然这样,我们在生成100~200范围内的数时是不是就可以直接把偶数给过滤掉了呢?
下面对上述的代码进行优化:

//打印素数——目标优化
int main()
{
	int i = 0;
	int n = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i += 2)
	{
		//功能二——生成范围
		for (n = 2; n < i; n++)
		{
			//功能三——判断素数
			if (0 == i % n)
			{
				break;
			}
		}
		//功能四——打印素数
		if (n == i || 1 == i)
		{
			printf("%-2d  ", i);
		}
	}
	return 0;
}

现在我们就完成了第一次优化,下面我们来探讨一下从范围上又应该如何优化;

优化范围

接下来我们再来思考一个问题,如果一个数不是素数,那是不是就说明这个数可以写成除i=1×i外的另一个等式i=a×b,此时a和b两个数要么一个大一个小,要么就相等;
假设a<=b,如果我们在2~(i-1)这个范围内能找到a的话是不是就说明了i是存在除1和它本身以外的其它约数,那这个a具体是在什么范围内呢?
根据a<=b这个条件我们可以两边同时乘以a得到a×a=a2<=b×a=i
此时式子两边同时开平方可以得到sqrt(a^2)=a<=sqrt(b×a)=sqrt(i)
这样我们就可以进一步确定x的范围是从2~sqrt(i)的,那我们可以继续优化上面的代码:

#include <stdio.h>
#include <math.h>//使用sqrt函数需引用的头文件
//打印素数——目标优化
int main()
{
	int i = 0;
	int n = 0;
	//功能一——生成目标数
	for (i = 1; i <= 100; i += 2)
	{
		//功能二——生成范围
		for (n = 2; n <= sqrt(i); n++)
		{
			//功能三——判断素数
			if (0 == i % n)
			{
				break;
			}
		}
		//功能四——打印素数
		if (n > sqrt(i) || 1 == i)
		{
			printf("%-2d  ", i);
		}
	}
	return 0;
}

注:sqrt——开平方的数学库函数,如果我们需要使用这个函数的话就需要引用头文件<math.h>

现在我们最后再测试一下:
打印素数3
可以看到,现在也是能够成功打印的。

这一题咱们就做完了,当然打印素数肯定还有更好的方法,朋友们如果你们有更好的方法可以分享在评论区,供大家参考一下。下面我们继续来看下一题;

数9的个数(1~100)

这题需要我们数9的个数,我们应该如何去数呢?下面我们来分析一下题目;

题目解析

在1~100中有9的数无非就两种情况——个位有9和十位有9。
我们现在要解决的就是如何找到个位和十位的9呢?
这时有朋友很快就能想到通过取模和整数除法。这是什么意思呢?下面我来解释一下;

找个位9

通过取模我们能够得到一个数的余数,如果我们的取模对象是10,那我们是不是就能得到个位上的具体数值了,比如13%10=3
这里我们通过11与10进行取模得到的余数为3,刚好就是13的个位。
按照这个思路,我们只需要将每个数跟10进行取模然后判断余数是否为9,是不是就能找到对应的带9的数了;

找十位9

在C语言中使用/时,如果两边的数都为整数,则实现的是整数除法,此时的结果为整数部分,如19/10=1
这里我们通过19与10相除得到的整数部分为1,刚好就是19的十位。
按照这个思路,我们只需要将每个数跟10进行相除然后判断结果是否为9,是不是就能找到对应的带9的数了;
有了这以上思路,我们就可以进行编码实现了;

代码实现

这一题的编码比较简单,我们需要完成以下几个功能:

功能一——生成1~100之间的数;
功能二——判断个位,并计数;
功能三——判断十位,并计数;
功能四——打印9的个数;

现在我们来一一实现这些功能;

功能一——生成目标数

这个问题又遇到了,此时我相信大家都已经轻车熟路了,我们通过循环来实现:

	int i = 0;
	//功能一——生成目标
	for (i = 1; i <= 100; i++)

接下来我们继续看下一个功能;

功能二——判断个位并计数

这里我们需要计数,所以我们还需要定义一个计数变量来完成,判断内容就是i与10取模是否为9:

	int i = 0;
	int count = 0;//计数变量
	//功能一——生成目标
	for (i = 1; i <= 100; i++)
	{
		//功能二——判断个位
		if (9 == i % 10)
		{
			count++;
		}
	}

下面我们继续实现下一个功能;

功能三——判断十位并计数

这里我们顺着功能二的思路,只需要将取模改为除就可以了:

	int i = 0;
	int count = 0;//计数变量
	//功能一——生成目标
	for (i = 1; i <= 100; i++)
	{
		//功能二——判断个位
		if (9 == i % 10)
		{
			count++;
		}
		//功能三——判断十位
		if (9 == i / 10)
		{
			count++;
		}
	}

功能四——打印9的个数

最后我们只需要在循环结束后将9的个数打印出来即可,这里我们还可以在循环中将个位有9的数和十位有9的数都打印出来,完整代码如下:

//数9的个数
int main()
{
	int i = 0;
	int count = 0;//计数变量
	//功能一——生成目标
	for (i = 1; i <= 100; i++)
	{
		//功能二——判断个位
		if (9 == i % 10)
		{
			count++;
			printf("%-2d  ", i);
		}
		//功能三——判断十位
		if (9 == i / 10)
		{
			count++;
			printf("%-2d  ", i);
		}
	}
	//功能四——打印9的个数
	printf("\n1~100中9的个数有%d个\n", count);
	return 0;
}

我们来看一下结果:
打印9的个数
可以看到很好的将9的个数记录了下来。
今天的最后习题到这里我们就全部介绍完了,希望对大家的解题上会有帮助,当然所有的习题并不是只有一种解法,朋友们你们可以尝试一下通过其它的方式来解题。

结语

今天的内容到这里也就全部结束了,下一篇要讲解的习题如下所示:

分数求和(1/1-1/2+1/3-1/4+1/5……+1/99-1/100)
求最大值(求10个整数中的最大值)
乘法口诀表(在屏幕上输出九九乘法表)

希望各位能在阅读的过程中不仅能够加深对分支与循环语句知识点的理解与运用,还能开拓新的解题思维,最后感谢各位的翻阅,咱们下一篇见!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值