# 第四章 循环控制

> Arietids Learn · 个人课程复习笔记
> 在线阅读：https://learn.arietids.net/courses/programming/chapter-04/

## 关键考点总览
1. for循环语句格式与执行流程
2. while循环语句格式与执行流程
3. do while循环语句格式与执行流程
4. 三种循环的相互转换
5. 逗号表达式的执行规则
6. break语句的作用与适用范围
7. continue语句的作用
8. 死循环的构造与退出
9. 枚举法的基本思想与实现
10. 贪婪法的基本思想与局限性

## 详细知识点

### 4.1 循环基础概念

#### 定义/语法
- **循环三要素**：循环初始条件、循环终止条件、循环控制变量的更新规则（趋向终止条件）  
  [来源：第4章 p3]

- **循环周期**：循环体所有语句的一次完全执行称为一个循环周期  
  [来源：第4章 p9，Notes]

#### 规则/规范
- 需要多次重复执行某任务的问题考虑使用循环来解决  
  [来源：第4章 p3]

- 计算机善于完成繁重的、但有规律的重复性计算，这是采用循环控制的内在逻辑  
  [来源：第4章 p2]

---

### 4.2 for循环

#### 定义/语法
- **for语句格式**：
  ```cpp
  for(表达式1; 表达式2; 表达式3)  语句
  ```
  - 表达式1：初始化循环控制变量
  - 表达式2：循环控制条件（可转换为bool的表达式；结果为0/false则退出循环）
  - 表达式3：每个循环周期结束后对循环控制变量的修正  
  [来源：第4章 p9]

#### 执行逻辑
- **for循环执行过程**：
  1. 计算表达式1（初始化循环控制变量）
  2. 计算表达式2（循环控制条件）
  3. 如果表达式2结果非0（true），执行循环体，然后计算表达式3，回到步骤2
  4. 如果表达式2结果为0（false），for语句执行结束  
  [来源：第4章 p9]

#### 代码模式
- **典型for循环示例**：
  ```cpp
  for (int i = 0; i < 100; i++)  ...   // 从0到99
  for (int i = 100; i > 1; i -= 2) ... // 从100递减，步长2
  ```
  [来源：第4章 p9]

- **从1加到100的标准写法**：
  ```cpp
  int i, s = 0;
  for (i = 1; i <= 100; ++i)  s += i;
  ```
  [来源：第4章 p10]

#### 规则/规范
- for括号内声明的循环变量（如 `for (int i = 1; ...)`）具有for语句作用域：它是一个新的局部变量，可遮蔽同名外部变量；若需要在循环外继续使用该变量，应在循环外定义  
  [来源：第4章 p10，Notes]

- **最佳实践**：使用标准格式，循环控制变量外围无定义；规则放在括号里，要执行的放在循环体里  
  [来源：第4章 p10，Notes]

- for循环语句很方便用于**确定循环次数**的循环控制  
  [来源：第4章 p14]

#### 易错点与辨析
- ⚠️ 变量s必须初始化为0，否则结果不确定  
  [来源：第4章 p10，Notes]

- ⚠️ for循环中的**表达式3是在所有循环内语句执行结束之后才执行的**，需特别注意执行顺序  
  [来源：第4章 p18，Notes]

---

### 4.3 逗号表达式

#### 定义/语法
- **逗号表达式格式**：
  ```cpp
  表达式1, 表达式2, ..., 表达式n
  ```
  [来源：第4章 p11]

#### 执行逻辑
- **逗号表达式执行过程**：
  1. 逗号运算符起分割作用
  2. 先执行表达式1，再执行表达式2，...，再执行表达式n（从左到右）
  3. 整个表达式的计算结果为**最后一个表达式的值**  
  [来源：第4章 p11]

#### 代码模式
- **示例**：若 a 初值为 0，则表达式 `a += 1, a += 2, a += 3, a += 4, a += 5` 的值为 **15**，且执行完后 a 的值也变为 **15**  
  [来源：第4章 p11]

---

### 4.4 while循环

#### 定义/语法
- **while语句格式**：
  ```cpp
  while (表达式)  语句
  // 循环体可以是复合语句或空语句
  ```
  [来源：第4章 p16]

#### 执行逻辑
- **while循环执行过程**：
  1. 计算表达式的值（作为循环控制条件）
  2. 如果为0，循环终止，执行while循环之后的语句
  3. 如果非0，执行整个循环体
  4. 重复上述过程  
  [来源：第4章 p16]

#### 代码模式
- **求e^x的级数展开**（当某一项的绝对值小于阈值时结束循环）：
  ```cpp
  ex = 0; p = 1; i = 0;
  while (fabs(p) > 1e-6) {
      ex += p;
      ++i;
      p = p * x / i;
  }
  ```
  [来源：第4章 p17]

---

### 4.5 for与while的相互转换

#### 规则/规范
- **for语句改写为while语句的方法**：
  ```cpp
  // for形式
  for(表达式1; 表达式2; 表达式3)  语句
  
  // 等价的while形式（若循环体中没有continue；如有continue需确保仍执行“表达式3”）
  表达式1;
  while(表达式2) {
      语句;
      表达式3;  
  }
  ```
  [来源：第4章 p18]

#### 代码模式
- **转换示例**：
  ```cpp
  // for形式
  for(double x = a + dlt/2; x < b; x += dlt)
      integral += (x*x + 5*x + 1) * dlt;
  
  // while形式
  double x = a + dlt/2;
  while(x < b) {
      integral += (x*x + 5*x + 1) * dlt;
      x += dlt;
  }
  ```
  [来源：第4章 p18]

---

### 4.6 do while循环

#### 定义/语法
- **do...while语句格式**：
  ```cpp
  do  语句  while (表达式);
  // 循环体可以是复合语句或空语句
  // 注意末尾有分号
  ```
  [来源：第4章 p20]

#### 执行逻辑
- **do...while执行过程**：**先执行循环体，然后判断循环条件**，如条件成立，继续循环，直到条件为假  
  [来源：第4章 p20]

#### 易错点与辨析
- ⚠️ **while与do while的核心区别**：
  - while：**先判断后执行**，循环体可能一次都不执行
  - do while：**先执行后判断**，循环体至少执行一次  
  [来源：第4章 p20]

#### 代码模式
- **输入数据累加直到输入0为止**：
  ```cpp
  total = 0;
  do {
      cout << "Input a number:";
      cin >> value;
      total += value;
  } while (value != 0);
  ```
  [来源：第4章 p20]

- **二分法求方程根**（x³+2x²+5x-1=0 在[-1,1]区间）：
  ```cpp
  double x, x1 = -1, x2 = 1, fx1, fx;
  do {
      fx1 = x1*x1*x1 + 2*x1*x1 + 5*x1 - 1;
      x = (x1 + x2) / 2;
      fx = x*x*x + 2*x*x + 5*x - 1;
      if (fx * fx1 > 0) x1 = x; else x2 = x;
  } while (fabs(fx) > 1e-7);
  ```
  [来源：第4章 p22]

---

### 4.7 死循环与中途退出

#### 定义/语法
- **死循环**：循环控制条件永远为真的循环  
  [来源：第4章 p24]

- **构造死循环的方式**：
  ```cpp
  for (;;);                  // 三个表达式均省略；其中表达式2省略时默认为true（死循环）
  for (;true;) ...           // 表达式2恒为true
  for (;1;) ...              // 表达式2恒为非0
  while (true) ...           // 条件恒为true
  ```
  [来源：第4章 p24]

#### 规则/规范
- 运行时跳出死循环：通常可用 **Ctrl+C**（命令行）或 IDE 的 **Stop/Terminate** 按钮中断（取决于运行环境）  
  [来源：第4章 p24]

- 死循环设置好退出机制后是有用的（如cin等待输入）  
  [来源：第4章 p24，Notes]

---

### 4.8 break语句

#### 定义/语法
- **break语句**：用于跳出一个循环体或switch体  
  [来源：第4章 p26]

#### 规则/规范
- **break的适用范围**：对于嵌套结构，break**只能跳出其所在的底层结构**  
  [来源：第4章 p26]

- break一般与if语句配合使用，if负责检查终止条件，条件满足后break结束循环  
  [来源：第4章 p25，Notes]

#### 代码模式
- **使用break的死循环模式**：
  ```cpp
  while (true) {
      // 提示用户并读入数据
      if (value == 标志) break;  // break语句跳出循环
      // 对数据作出处理
  }
  ```
  [来源：第4章 p25]

#### 例题结论
- **【执行结果分析】** 嵌套结构中switch内的break退出switch，while内的break退出while：
  ```cpp
  for (int i = 0; i < 2; i++) {
      while(1) {
          switch(1) {
              case 1: break;  // 退出switch
              case 2: cout << "switch" << endl;
          }
          cout << "while" << endl;
          break;  // 退出while
          cout << "behind while break" << endl;  // 不执行
      }
      cout << " for loop" << endl;
  }
  ```
  输出结果：
  ```
  while
   for loop
  while
   for loop
  ```
  [来源：第4章 p26]

#### 易错点与辨析
- ⚠️ 多层嵌套循环要跳出所有循环，需要**多个break配合标志变量**：
  ```cpp
  bool flag = false;
  for (...) {
      for (...) {
          for (...) {
              if (条件) { flag = true; break; }
          }
          if (flag) break;
      }
      if (flag) break;
  }
  ```
  [来源：第4章 p37]

- 或者将**标志变量放入循环条件**：
  ```cpp
  for (dt = 1; dt <= 4 && !flag; ++dt) {
      for (hz = 1; hz <= 4 && !flag; ++hz) {
          ...
      }
  }
  ```
  [来源：第4章 p38]

---

### 4.9 continue语句

#### 定义/语法
- **continue语句**：跳过本轮循环周期中剩余的语句，直接开始下一轮循环  
  [来源：第4章 p25]

#### 规则/规范
- continue与break的区别：break**终止循环**，continue**跳过当前轮**  
  [来源：第4章 p25，Notes]

- 在for循环中，continue会**跳过之后语句，直接执行表达式3**  
  [来源：第4章 p25]

#### 代码模式
- **求0至x之间偶数的和**：
  ```cpp
  int sum, i;
  for (sum = 0, i = 0; i <= x; i++) {
      if (i % 2 != 0) continue;  // 奇数跳过
      sum += i;
  }
  ```
  [来源：第4章 p25]

---

### 4.10 枚举法

#### 定义/语法
- **枚举法**：对所有可能的情况一一去尝试，直到找到正确的答案。非常适合利用计算机求解  
  [来源：第4章 p33]

#### 代码模式
- **买水果问题**（50元买100个水果：西瓜5元、苹果1元、桔子1元3个。下述代码默认每种水果至少买1个；若允许西瓜为0，则还存在 mellon:0 apple:25 orange:75）：
  ```cpp
  for (mellon = 1; mellon < 10; ++mellon)
      for (apple = 1; apple < 50 - 5*mellon; ++apple) {
          orange = 3 * (50 - 5*mellon - apple);
          if (mellon + apple + orange == 100) {
              cout << "mellon:" << mellon << ' ';
              cout << "apple:" << apple << ' ';
              cout << "orange:" << orange << endl;
          }
      }
  ```
  输出结果：
  ```
  mellon:1 apple:18 orange:81
  mellon:2 apple:11 orange:87
  mellon:3 apple:4 orange:93
  ```
  [来源：第4章 p34]

- **罗列2到100的素数（或：输出100以内素数）**：
  ```cpp
  for (int i = 2; i <= 100; i++) {
      bool flag = true;
      for (int j = 2; j < i; j++) {
          if (i % j == 0) {
              flag = false;
              break;
          }
      }
      if (flag) cout << i << endl;
  }
  ```
  [来源：第4章 p39]

#### 易错点与辨析
- 【整理自 PPT】枚举法中，可以按一个约束条件列出所有可行情况，然后检查每个可能解是否满足其他约束条件  
  [来源：第4章 p33]

- 【整理自 PPT】四大湖问题中，布尔条件可以参与加法运算：`(a == 1) + (b == 4) + (c == 3)` 表示满足条件的个数  
  [来源：第4章 p35]

---

### 4.11 贪婪法

#### 定义/语法
- **贪婪法**：在求解过程的每一步都选取一个**局部最优的策略**，把问题规模缩小，最后把每一步的结果合并起来形成一个全局解  
  [来源：第4章 p41]

#### 代码模式
- **硬币找零问题**（1分、2分、5分、1角硬币，找K分零钱）：
  ```cpp
  int money = K;
  int onejiao = 0, fivefen = 0, twofen = 0, onefen = 0;
  while (money >= 10) { ++onejiao; money -= 10; }
  while (money >= 5)  { ++fivefen; money -= 5; }
  while (money >= 2)  { ++twofen;  money -= 2; }
  while (money >= 1)  { ++onefen;  money -= 1; }
  ```
  [来源：第4章 p42]

#### 易错点与辨析
- ⚠️ **贪婪法不一定得到最优解！** 如果局部最优无法带来全局最优，就不具备贪心选择的性质  
  [来源：第4章 p43]

- **反例**：硬币面值为1分、3分、9分、1角时，找27分：
  - 贪婪法：27 = 10 + 10 + 3 + 3 + 1 = **5个硬币**
  - 最优解：27 = 9 + 9 + 9 = **3个硬币**  
  [来源：第4章 p43]

---

### 4.12 本章总结

#### 判定条件/等价表述
- 计算机的强项是不厌其烦地做同样的操作，这是通过循环语句实现的  
  [来源：第4章 p44]

- **三种循环语句**：for、while、do while  
  [来源：第4章 p44]

- **枚举法**：对某些问题，在寻找解时需要检查所有可能的方案，从中找出可行解  
  [来源：第4章 p44]

- **贪婪法**：可用于求问题的最优解，但**不一定对所有问题都能得到最优解**  
  [来源：第4章 p44]

---

### 4.13 习题与答案

#### 例题结论

1. **xyz + yzz = 532**，x、y、z各代表不同数字，求x、y、z。  
   **答案**：xyz = 321（即 x=3, y=2, z=1）  
   [来源：第4章 p45，Notes]

2. **输出N行等腰三角形**：输入整数N，输出由'*'组成的等腰三角形，下行比上行多两个'*'，尖角朝上。  
   **提示**：设输入为 N（行数），第 i 行（i 从 0 开始）输出：先输出 (N - i - 1) 个空格，再输出 (2*i + 1) 个 '*'；行尾空格可省略  
   [来源：第4章 p45，Notes]

3. **求x的平方根**（迭代法）：  
   - (1) Root = x/2  
   - (2) 检查 root*root 和 x 的差  
   - (3) 按 root = (root + x/root)/2 更新  
   - (4) 重复直到误差小于1%  
   [来源：第4章 p45]

---

## 学习目标核对

根据PPT第7页的学习目标：
- ✅ for, while, do while：**熟练掌握一种**循环结构
- ✅ 另外两种：**会读即可**
- ✅ 会使用 **break、continue** 编程

[来源：第4章 p7，学习目标]
