流程控制之循环结构教学讲义


1. 循环结构简介

1.1 什么是循环结构

循环结构(Loop Structure):根据某个条件是否满足,重复执行一段代码块。它使计算机能够高效地处理大量重复性工作。

现实世界中存在大量重复操作:“从1加到100”、“重复打印10次Hello”……如果没有循环,我们需要把相同的代码写100遍,这显然不可行。循环结构正是为解决这类问题而诞生的。

图示:循环执行流程

           ┌─────────────┐
           │  循环条件    │
           └──────┬──────┘
                  │
         ┌────────┴────────┐
         │ 真(true)        │ 假(false)
         ▼                 ▼
   ┌───────────┐     ┌───────────┐
   │  循环体    │     │  退出循环  │
   └─────┬─────┘     └───────────┘
         │
         └──→ 返回判断条件

1.2 为什么需要循环结构

  • 避免代码重复:用几行代码代替成百上千行的重复代码。
  • 处理批量数据:如输入100个学生的成绩、计算1~100的和。
  • 实现复杂算法:排序、查找、穷举、模拟等都依赖循环。

2. 循环结构的类型

C++ 提供了四种主要的循环语句。

2.1 while 循环(前置条件循环)

格式:先判断条件,条件为真时才执行循环体;可能一次都不执行。

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

示例:计算 1 + 2 + ... + n。

int i = 1, sum = 0;
while (i <= n) {
    sum += i;
    i++;
}

图示:

      判断条件
         │
     ┌───┴───┐
    是      否
     │       │
     ▼       ▼
   执行循环体  退出
     │
     └──→ 返回判断

2.2 do-while 循环(后置条件循环)

格式:先执行一次循环体,再判断条件;至少执行一次。

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

示例:同样计算 1~n 的和。

int i = 1, sum = 0;
do {
    sum += i;
    i++;
} while (i <= n);

区别:while 可能一次都不执行,do-while 至少执行一次。适用于“先执行再判断”的场景(如菜单输入)。

2.3 for 循环(计数循环)

格式:将循环变量初始化、条件判断、变量更新三部分集中在一起,结构清晰。

for (循环变量初始化; 循环条件; 循环变量更新) {
    循环体;
}

示例:计算 1~n 的和。

int sum = 0;
for (int i = 1; i <= n; i++) {
    sum += i;
}

执行顺序:

  1. 执行初始化(仅一次)
  2. 判断循环条件,为真则进入循环体,否则退出
  3. 执行循环体
  4. 执行变量更新
  5. 返回第2步

for 循环的各种变形:

  • 省略初始化:int i = 1; for (; i <= n; i++)
  • 省略更新:for (int i = 1; i <= n; ) { ... i++; }
  • 无限循环:for (;;) { ... }

2.4 无限循环

当循环条件永远为真时,循环永不结束。通常配合 break 在内部退出。

while (true) {
    // 循环体
    if (退出条件) break;
}

或 for (;;) { ... }

示例:使用无限循环计算 1~n 的和。

int i = 1, sum = 0;
while (true) {
    sum += i;
    i++;
    if (i > n) break;
}

注意:无限循环必须有退出机制(break 或 return),否则程序会“卡死”。

2.5 循环嵌套

在一个循环的循环体内再包含另一个完整的循环结构,称为循环嵌套。外层循环每执行一次,内层循环会完整执行一轮。

示例:输入一个 n×m 的矩阵。

for (int i = 1; i <= n; i++) {      // 外层循环:行
    for (int j = 1; j <= m; j++) {  // 内层循环:列
        cin >> num;
    }
}

复杂度:外层循环 n 次,内层循环 m 次,总循环次数为 n×m。

2.6 break 和 continue

  • break:强制终止整个循环,跳出当前循环体(只影响所在的最内层循环)。
  • continue:跳过本次循环剩余代码,立即进入下一次循环判断。

示例:输出 5~10(跳过 1~4)。

for (int i = 1; i <= 10; i++) {
    if (i < 5) continue;
    cout << i << endl;
}
// 输出:5 6 7 8 9 10

示例:素数判定(找到第一个因子后立即退出)。

bool isPrime = true;
for (int i = 2; i * i <= n; i++) {
    if (n % i == 0) {
        isPrime = false;
        break;   // 无需继续检查
    }
}

3. 经典问题示例

3.1 角谷猜想(3n+1 问题)

任意正整数,如果是奇数则乘以3加1,如果是偶数则除以2,重复操作,最终都会变为1。

int n;
cin >> n;
while (n > 1) {
    if (n % 2 == 1) {
        n = n * 3 + 1;
    } else {
        n /= 2;
    }
    cout << n << endl;
}

3.2 百钱买百鸡

公鸡5元,母鸡3元,小鸡1/3元(即3只1元)。用100元买100只鸡,求各买多少只。

优化解法(双重循环 + 剪枝):

for (int i = 1; i <= 20; i++) {      // 公鸡最多20只(5*20=100)
    for (int j = 1; j <= 33; j++) {  // 母鸡最多33只(3*33=99)
        int k = 100 - i - j;         // 小鸡数量由总数推导
        if (5 * i + 3 * j + k / 3.0 == 100) {
            cout << "公鸡:" << i << ",母鸡:" << j << ",小鸡:" << k << endl;
        }
    }
}

3.3 鸡兔同笼

已知头数和脚数,求鸡兔各多少只。数学解法 O(1):

int head, foot;
cin >> head >> foot;
int rabbit = (foot - 2 * head) / 2;  // 或 (4*head - foot)/2
int chicken = head - rabbit;
cout << "兔:" << rabbit << ",鸡:" << chicken << endl;

4. 课堂例题

例题1:求最大公约数(辗转相除法)

题目描述

输入两个正整数 a 和 b(1 ≤ a,b ≤ 10⁹),求它们的最大公约数(GCD)。

输入:一行两个整数。 输出:一个整数,表示最大公约数。

思路解析

辗转相除法(欧几里得算法):gcd(a, b) = gcd(b, a % b),重复直到余数为0。这个过程天然适合循环实现。可以用 while 循环不断取余,直到余数为0,此时的除数即为最大公约数。

代码实现

#include <bits/stdc++.h>
using namespace std;

int main() {
    int a, b;
    cin >> a >> b;
    
    // 辗转相除
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    cout << a << endl;
    return 0;
}

注:也可以使用 __gcd(a, b) 内置函数,但理解算法原理更重要。

例题2:水仙花数

题目描述

水仙花数是指一个三位数,其各位数字的立方和等于该数本身。例如:153 = 1³ + 5³ + 3³。输出所有的水仙花数,每个数占一行。

输出:所有符合条件的三位数,每行一个。

思路解析

遍历100~999之间的所有数,对每个数提取百位、十位、个位,计算立方和并判断是否等于原数。循环结构(for)非常适合这种“枚举”场景。

代码实现

#include <bits/stdc++.h>
using namespace std;

int main() {
    for (int i = 100; i <= 999; i++) {
        int a = i / 100;          // 百位
        int b = i / 10 % 10;      // 十位
        int c = i % 10;           // 个位
        if (a*a*a + b*b*b + c*c*c == i) {
            cout << i << endl;
        }
    }
    return 0;
}

例题3:打印九九乘法表

题目描述

输出九九乘法表,格式如下(每个算式占4个字符宽度,左对齐):

1*1=1   
1*2=2   2*2=4   
1*3=3   2*3=6   3*3=9   
...
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81

思路解析

双重循环:外层控制行数(1~9),内层控制列数(1~行数)。注意每行输出结束后换行。使用 setw(4) 控制宽度。

代码实现

#include <bits/stdc++.h>
using namespace std;

int main() {
    for (int i = 1; i <= 9; i++) {          // 行(乘数1)
        for (int j = 1; j <= i; j++) {      // 列(乘数2)
            cout << j << "*" << i << "=" << setw(2) << i*j << " ";
        }
        cout << endl;
    }
    return 0;
}

扩展:可以尝试输出右上三角、右下三角等变体。

5. 课后练习

以下题目建议在洛谷(luogu.com.cn)上完成,涵盖循环结构的各种应用。

序号 题目名称 链接 知识点
1 【深基4.例2】找最小值 P5718 循环 + 打擂台
2 【深基4.例3】分类平均 P5719 循环 + 条件统计
3 【深基4.例4】一尺之棰 P5720 while 循环
4 【深基4.例6】数字直角三角形 P5721 循环嵌套 + 输出格式
5 质数口袋 P5723 循环 + 素数判定 + break
6 阶乘之和 P1009 循环嵌套(高精度可选)
7 级数求和 P1035 循环累加直到满足条件
8 角谷猜想 P5727 while 循环 + 条件判断
9 求极差 P5729 循环求最大最小值
10 [NOIP2002 普及组] 过河卒 P1002 循环 + 二维数组(难度稍高)

建议:从简单的累加、累乘开始,逐步挑战循环嵌套和复杂逻辑。

6. 总结

循环类型 适用场景 特点
while 循环次数未知,需先判断条件 可能一次都不执行
do-while 至少需要执行一次的场景(如菜单) 先执行后判断
for 循环次数已知(如遍历数组、计数) 结构清晰,推荐优先使用
循环嵌套 处理二维数据、打印图形、穷举组合 注意内外层关系,总次数为乘积
break 提前退出循环(如找到目标后) 只跳出当前最内层循环
continue 跳过本次循环的剩余部分,继续下一次 不跳出循环

核心技巧:

  • 避免死循环:确保循环条件最终为假。
  • 注意循环变量的边界:<= 还是 < 要仔细。
  • 嵌套循环中,内层循环的循环变量不要与外层冲突。
  • 能用 for 尽量用 for,可读性更好。

教练寄语:循环是编程中最强大的结构之一。请务必多写代码,尤其是课后练习。当你能够熟练使用循环解决“累加、累乘、枚举、模拟”等问题时,你已经掌握了程序设计的核心能力。

参考资料

  • 【书籍】《信息学奥赛一本通》第3章:循环结构程序设计
  • 【书籍】《C++ Primer Plus》第5章:循环和关系表达式
  • 【在线资源】OI Wiki - 循环结构