2.3 周而复始的循环
知识点
1.为什么需要循环
重复性的工作:求 1 到 100 的和、打印 50 名学生的成绩、判断 1 到 1000 中的素数……如果一条一条写语句,代码量不可接受。
循环结构:在满足条件的情况下,反复执行同一段语句。
流程图特征:流程线从后往前回到某一步,形成回路,且必须有出口(否则违反算法的有穷性)。
循环三要素(写循环前必须先想清楚):
- 循环变量的初值;
- 循环条件(什么时候继续);
- 循环变量如何更新(怎么变、变多少)。
三要素缺一个,循环就转不起来或停不下来。
2.for 循环与 range 函数
(1)基本格式
for 循环变量 in 序列:
循环体
执行流程:从序列中依次取出每个元素赋给循环变量,每取一个就执行一次循环体,取完为止。
(2)range 函数(重点,必考)
格式:range(起始值, 终止值, 步长)
| 写法 | 产生的序列 | 循环次数 |
|---|---|---|
range(5) | 0, 1, 2, 3, 4 | 5 次(默认从 0 开始) |
range(1, 6) | 1, 2, 3, 4, 5 | 5 次(含 1,不含 6) |
range(1, 10, 2) | 1, 3, 5, 7, 9 | 5 次 |
range(10, 0, -2) | 10, 8, 6, 4, 2 | 5 次(步长为负,倒着走) |
range(0) | 空 | 0 次(循环体不执行) |
三条核心规则:
- 左闭右开:包含起始值,不包含终止值——
range(1, 6)是 15,不是 16; - 步长默认 1,可正可负,不能为 0;
- 循环次数 =
(终止值 - 起始值) / 步长的向上取整(正步长时)。
记忆口诀:"含头不含尾"——这是 Python 循环最容易被扣分的地方。
常用对照:
| 需求 | 写法 |
|---|---|
| 1 到 100(含 100) | range(1, 101) |
| 1 到 n(含 n) | range(1, n + 1) |
| 0 到 n-1 | range(n) |
| 偶数 2~100 | range(2, 101, 2) |
| 倒序 100~1 | range(100, 0, -1) |
(3)遍历字符串与列表
for ch in "Python":
print(ch) # P y t h o n 各占一行
for name in ["张三", "李四", "王五"]:
print(name)
for … in 的本质:遍历(逐个访问)序列中的元素,不需要关心下标。
需要下标时用 range(len(...)):
s = "hello"
for i in range(len(s)):
print(i, s[i])
3.while 循环
(1)基本格式
while 条件:
循环体 # 循环体中必须有让条件趋于假的操作
执行流程:先判断条件→ 成立则执行循环体 → 再回到条件判断 → ……直到条件不成立。
特点:先判断,后执行;条件一开始就不成立时,循环体一次也不执行。
(2)与 for 的选择
| 场景 | 推荐 | 原因 |
|---|---|---|
| 已知循环次数(1~100、数组遍历) | for | 次数明确,代码简洁,不会死循环 |
| 次数未知,靠条件结束(直到猜对、直到精度满足) | while | 无法预先知道要转几次 |
| 必须至少执行一次 | while(或 do-while 思想) | 先做再判断 |
记忆:"次数确定用 for,条件控制用 while"。
(3)while 的三个必备动作
i = 1 # ① 循环变量赋初值
while i <= 100: # ② 循环条件
total = total + i
i = i + 1 # ③ 循环变量更新(漏了就死循环)
死循环:条件永远为真,程序卡住不结束。
i = 1
while i <= 100:
print(i) # 忘了 i = i + 1,永远打印 1
排查死循环:检查循环变量有没有更新、更新方向对不对(条件写 i <= 100 却写 i = i - 1 也是死循环)、条件里有没有用到不会变的量。
强制结束:在交互式环境或终端按 Ctrl + C。
4.两种基本模式:累加与计数
(1)累加(求和、求积)
模板:
total = 0 # 累加器初值必须是 0(求积则为 1)
for i in range(1, 101):
total = total + i # 等价于 total += i
print(total) # 5050
为什么初值必须是 0:0 + 任何数 不改变结果;若初值写成 1,结果会多 1。
求积的初值必须是 1(1 * 任何数 不变),这是阶乘的写法:
n = int(input("n = "))
f = 1
for i in range(1, n + 1):
f = f * i
print(f"{n}! = {f}")
(2)计数(统计个数)
模板:
count = 0
for i in range(1, 101):
if i % 3 == 0:
count += 1 # 满足条件才加 1
print("1~100 中 3 的倍数有", count, "个")
要点:计数器初值 0,在 if 内部才加 1(写在 if 外面就变成统计总次数)。
(3)求最值
模板:先把第一个数当作"当前最大",再逐个比较更新。
nums = [78, 92, 65, 88, 95]
big = nums[0]
for x in nums:
if x > big:
big = x
print("最大值:", big)
易错:初值不能设成 0(若全是负数,结果会错),应取序列的第一个元素。
5.break 与 continue
| 语句 | 作用 | 类比 |
|---|---|---|
| break | 立即结束整个循环,跳到循环之后的语句 | "不干了,走人" |
| continue | 结束本次循环,跳过剩余语句,直接进入下一次 | "这次先跳过,继续下一个" |
# break:找到第一个能被 7 整除的数就停
for i in range(1, 100):
if i % 7 == 0:
print("找到了:", i)
break # 输出 7,然后整个循环结束
# continue:跳过 3 的倍数
for i in range(1, 11):
if i % 3 == 0:
continue # 跳过 3、6、9
print(i, end=" ") # 输出 1 2 4 5 7 8 10
break 与 return 的差别:break 只跳出当前这一层循环;函数里的 return 会直接结束整个函数(见 2.4)。
循环的 else 子句(可了解):for … else 或 while … else 中的 else 部分,在循环正常结束(不是被 break 中断)时执行。常用于"找素数、找因子"的判定,考试中较少出现。
6.循环嵌套
定义:循环体中再写一个循环。
执行规律:外层走一步,内层走完一整圈。总执行次数 = 外层次数 × 内层次数。
# 2-7 九九乘法表
for i in range(1, 10): # 外层:行
for j in range(1, i + 1): # 内层:列,每行到 i 为止
print(f"{j}×{i}={i*j}", end="\t")
print() # 一行结束换行
要点:
- 内层循环的 range 可以用外层变量(如
range(1, i + 1)),这是打印三角形、乘法表的关键; - 每层缩进各不相同(外层 4 空格,内层 8 空格);
print()空打印用于换行。
常见图形题:
# 打印 5 行金字塔星号
for i in range(1, 6):
print(" " * (5 - i) + "*" * (2 * i - 1))
嵌套的效率:两层嵌套的时间是平方级增长(n × n),三层就是立方级——这是 4.1 讲时间复杂度的直观例子。
7.典型算法(本小节能力落点)
(1)穷举(枚举)
思路:把所有可能的情况一一列举,逐个检验,符合条件的就输出。
# 2-8 百钱百鸡(简化版):公鸡 5 元、母鸡 3 元、小鸡 1 元 3 只,100 元买 100 只
for x in range(0, 21): # 公鸡最多 20 只
for y in range(0, 34): # 母鸡最多 33 只
z = 100 - x - y
if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100:
print(x, y, z)
要点:穷举的关键是"缩小枚举范围"(用总数量约束减少一层循环),否则效率极低。
(2)辗转相除法(求最大公约数)
# 2-9 求 m、n 的最大公约数
m, n = 48, 18
while n != 0:
m, n = n, m % n
print("最大公约数:", m) # 6
原理:gcd(m, n) = gcd(n, m % n),直到余数为 0,此时的除数就是最大公约数。
(3)判断素数
n = int(input("请输入一个大于 1 的整数:"))
is_prime = True
for i in range(2, int(n ** 0.5) + 1): # 只需试到根号 n
if n % i == 0:
is_prime = False
break
print("是素数" if is_prime else "不是素数")
两个优化点:只需试到 n 的平方根(因数成对出现);找到因数就 break。
(4)斐波那契数列(递推)
a, b = 1, 1
for i in range(10):
print(a, end=" ")
a, b = b, a + b
要点:用两个变量滚动往前推,是"迭代"思想的雏形(第 4 单元展开)。
(5)水仙花数(穷举 + 数位拆分)
for n in range(100, 1000):
a = n // 100 # 百位
b = n // 10 % 10 # 十位
c = n % 10 # 个位
if a**3 + b**3 + c**3 == n:
print(n, end=" ")
8.易错点清单
| 序号 | 错误 | 说明 | 正确 |
|---|---|---|---|
| 1 | for i in range(1, 100) 想求 1~100 的和 | 少了 100(左闭右开) | range(1, 101) |
| 2 | while 缺循环变量更新 | 死循环 | 循环体内加 i = i + 1 |
| 3 | 累加器初值写 1 | 结果多 1 | 初值 0 |
| 4 | 求积初值写 0 | 结果永远是 0 | 初值 1 |
| 5 | 计数器 count += 1 写在 if 外 | 统计的是总次数 | 放进 if 内 |
| 6 | 求最大值初值设 0 | 全负数时出错 | 取第一个元素 |
| 7 | break 与 continue 混用 | 一个"整个退出"、一个"跳过本次" | 按语义选 |
| 8 | 循环体缩进不一致 | IndentationError 或逻辑错 | 统一 4 空格,逐层递增 |
| 9 | range(1, 10, -1) | 步长为负但起始小于终止 → 空循环 | 用 range(10, 0, -1) |
| 10 | range(0) | 一次也不执行 | 确认边界是否写错 |
| 11 | 循环嵌套中内外层缩进相同 | 内层不在外层里 | 内层多缩进 4 空格 |
| 12 | 在循环里反复 input() 却没提示 | 用户不知道要输几次 | 加提示或用固定次数 |
9.速查表
| 序号 | 考点 | 结论 |
|---|---|---|
| 1 | 循环三要素 | 初值、条件、更新 |
| 2 | for 语法 | for 变量 in 序列: |
| 3 | range(5) | 0~4,共 5 次 |
| 4 | range(1, 6) | 1~5,共 5 次 |
| 5 | range 规则 | 左闭右开(含头不含尾) |
| 6 | range(1, 101) | 1 到 100 |
| 7 | range(10, 0, -2) | 10 8 6 4 2 |
| 8 | while 特点 | 先判断后执行,可能一次都不执行 |
| 9 | 次数确定用 | for |
| 10 | 条件控制用 | while |
| 11 | 死循环原因 | 循环变量未更新或方向错 |
| 12 | 累加器初值 | 0 |
| 13 | 累乘(阶乘)初值 | 1 |
| 14 | 求最值初值 | 第一个元素 |
| 15 | break | 结束整个循环 |
| 16 | continue | 跳过本次,继续下一次 |
| 17 | 嵌套执行次数 | 外层次数 × 内层次数 |
| 18 | 九九乘法表内层 | range(1, i + 1) |
| 19 | 素数判断优化 | 试到 根号 n,找到因数 break |
| 20 | 最大公约数 | 辗转相除(m, n = n, m % n) |
| 21 | 穷举优化关键 | 缩小枚举范围 |
| 22 | 强制终止运行 | Ctrl + C |
10.课堂提示(讲课参考)
- 第一节课只讲
for求 1~100 的和,让学生手算 5050 的高斯法,再用循环实现——"机器替人重复"的感受最直接。 range边界用"数台阶"比喻:从第 1 级走到第 6 级,实际只走 5 步(1、2、3、4、5),第 6 级是"终点不算"。- 死循环现场演示:漏写
i = i + 1,屏幕疯狂滚动,再教 Ctrl+C 终止,学生一辈子忘不了。 - 九九乘法表是嵌套的最佳载体:先打印 5 行 5 列方阵,再改成三角形(内层
range(1, i+1)),改一个参数就懂"内外层联动"。 - 把"累加/计数/求最值"三个模板做成卡片让学生背,后面第 4 单元的所有算法题都靠这三个模板打底。