8.7 KiB
C++ for 循环(二) 讲义
1. 引言
问题引入
在上一节课,我们学习了 for 循环,并用它解决了“重复打印N次”的问题,我们知道 for 循环是一个强大的计数器。但如果只是用它来数数,那可就太小看它了!
现在,我们来看另一个问题:
老师现在要大家一个一个地报上自己的学号,但我不知道咱们班有多少同学。在我听完所有人的学号后,我需要立刻说出那个最小的学号。
作为老师,我应该怎么做?需要把几十个学号都记在脑子里吗?太难了!我们的大脑会自动采用一种更聪明的策略:只记住“到目前为止听到的那个最小学号”,然后用新报上来的学号和这个“记录”进行比较。
价值阐述
今天我们将要学习的,是 for 循环在算法中最常见的三种应用模式:
- 打擂台模式:用于在一堆数据中寻找“最”值(最大、最小、最长、最强...)。
- 累加模式:用于对一连串数据进行求和、求积等累积性计算。
- 分类统计模式:用于遍历数据,并根据不同条件将它们归类、分别统计。
掌握了这三大模式,就能解决诸如数据分析、统计、模拟等一大批实际问题。
2. 核心思想与模式
模式一:打擂台算法 —— 寻找“最”值
这是解决“求最大/最小值”问题的模板。让我们用书本上例4-2 “找最小值”的例子,来学习这个算法。
核心思想:==只记住当前最优值,然后不断与新的挑战者进行比较==。
步骤拆解:
-
【搭建擂台】
- 比喻:比赛开始前,我们需要一个擂主。为了保证第一个挑战者一定能赢,我们故意请一个最弱的虚拟擂主站在台上。
- 代码实现:我们需要一个变量
min_val来扮演“擂主”。因为我们要找最小值(越小越强),所以最弱就意味着一个非常大的数。 int min_val = 100000000;// 一个足够大的初始值
-
【挑战者依次上台】
- 比喻:n个选手排好队,一个接一个上台挑战。
- 代码实现:我们用
for循环n次,每次在循环体内读入一个新的“挑战者”。 for (int i = 0; i < n; i++) { cin >> tmp; ... }
-
【判断胜负,更新擂主】
- 比喻:如果挑战者比擂主“更强”(在这里是数值更小),他就挑战成功,成为新擂主。否则,擂主守擂成功。
- 代码实现:在循环体内,用一个
if语句来判断。 if (tmp < min_val) { min_val = tmp; }
-
【比赛结束,留在最后的就是冠军】
- 比喻:所有人都挑战完毕后,最后还站在台上的,就是真正的冠军。
- 代码实现:当
for循环结束后,min_val变量里存储的值,就是我们要找的最小值。直接输出它即可。
模式二:累加模式 —— 求和
核心思想:准备一个容器变量,在循环中不断将自身和新的东西加在一起,最终越滚越大。
步骤拆解(往存钱罐里放硬币):
-
【准备一个空的存钱罐】
- 比喻:在开始存钱之前,你必须有一个空的存钱罐。
- 代码实现:定义一个求和变量
sum,并必须将它初始化为0。这个操作必须在循环外完成。 int sum = 0;
-
【确定要放哪些硬币】
- 比喻:我们要依次放入面值为 1, 2, 3, ..., 100 的硬币。
- 代码实现:用
for循环来生成 1 到 100 这一串数字。 for (int i = 1; i <= 100; i++) { ... }
-
【放硬币】
- 比喻:把当前这枚硬币
i丢进存钱罐sum。 - 代码实现:存钱罐里新的总数,等于它原来的数,加上当前硬币的面值。
sum = sum + i;(或简写为sum += i;)
- 比喻:把当前这枚硬币
关键:求和变量 sum 的初始化必须在循环外部。如果放在循环内部,它每一轮都会被“清空”回0,那永远也攒不下钱!
3. 分类统计模式(重要)
例如想象一个大型的快递分拣中心需要分类统计:
- 有一个传送带,上面源源不断地运来包裹。
- 传送带旁边站着一个分拣员,他的任务是看每个包裹的目的地,然后把它放到对应的城市货车里(比如北京、上海、广州)。
- 当所有包裹都处理完后,我们就可以很轻松的统计出每辆对应的货车里装了多少包裹。
在编程中,这个模式的逻辑是完全一样的,我们通过书本上实际的案例进行讲解。
案例:例4-3 分类求平均数
问题:从1到n这些数中,是k的倍数分为A类,不是的分为B类。求A、B两类数各自的平均数。
分析:for 循环像一个传送带,负责把1到n的数字一个一个送过来;循环内部的 if/else 像一个分拣员,判断这个数字该去A队还是B队。
总结起来就是:分类统计模式中 ==for 循环负责遍历所有的数据,而 if/else 判断负责对这些数据挨个进行分类并依照规则进行处理==。
分步演示
-
准备工作(循环外)
- 我们需要统计A、B两队的“总分”和“人数”,才能求平均分。所以,我们需要4个“存钱罐”!
double Asum = 0, Bsum = 0;int Acount = 0, Bcount = 0;
-
循环
- 我们需要遍历从1到n的所有数字。
for (int i = 1; i <= n; i++) { ... }
-
分类进行处理
- 每从传送带上拿到一个数字
i,就要判断它是不是k的倍数 (i % k == 0)。 - 使用
if-else结构:- 如果为真,是A队成员。就把
i累加到Asum,并让Acount加1。 - 否则,是B队成员。就把
i累加到Bsum,并让Bcount加1。
- 如果为真,是A队成员。就把
- 每从传送带上拿到一个数字
-
计算结果(循环后)
- 当循环结束,我们就得到了两队各自的总分和人数。
- 分别计算平均分
Asum / Acount和Bsum / Bcount并输出。
4. 代码实现
找最小值(打擂台)
int main() {
int n;
cin >> n; // 输入数字的总个数
int min_val = 100000000; // 储存擂主,初始化为极大值,确保第一个人能打败它
int tmp; // 用于存放当前的挑战者
for (int i = 0; i < n; i++) {
cin >> tmp; // 读入一个挑战者
// 如果挑战者更小,就更新擂主
if (tmp < min_val) {
min_val = tmp;
}
}
// 循环结束,输出最终的擂主
cout << min_val << endl;
return 0;
}
求1到100的和(累加)
int main() {
// 准备一个空的存钱罐
int sum = 0;
// 循环生成 1 到 100 的硬币
for (int i = 1; i <= 100; i++) {
// 把当前的硬币 i, 放入存钱罐 sum
sum += i;
}
// 循环结束后,输出存钱罐里的总数
cout << sum;
return 0;
}
5. 深化
变量的==作用域==
这是一个非常重要的概念!
- 循环内的临时工:在
for (int i = 0; ...)中定义的变量i,它的“生命周期”仅限于这个for循环。一旦循环结束,变量i就会消失,在循环外部无法使用它。 - 循环外的长期工:像
sum,min_val,Acount这些需要在循环结束后仍然保留其计算结果的变量,必须在循环外部定义。
工具箱:min() 和 max() 函数
对于打擂台算法中的找到两个数中的最小值,C++提供了一个更便捷的工具。min(a, b) 函数会直接返回a和b中较小的那一个。
- 使用前提:需要包含头文件
#include <algorithm>。 - 简化代码:打擂台算法中的核心判断
if (tmp < min_val) { min_val = tmp; }可以被简化为一句话:min_val = min(min_val, tmp); - 这句话的意思是:“新的擂主将会被赋值为‘旧擂主’和‘挑战者’中更小的那一个”,逻辑等价,但代码更简洁。
6. 总结
- 打擂台模式:
初始化最值 -> 循环遍历打擂台 -> if判断更新,用于求最大/最小值。 - 累加模式:
初始化累加器=0 -> 循环遍历 -> sum += i,用于求和。 - 分类统计模式:
for循环提供数据,if-else进行分类处理,而累加器分别统计不同的分类数量。 - 变量定义位置:准备工作(初始化)在循环外,重复性处理在循环内。