是不是每次看到“循环”就头大?for、while绕来绕去,break、continue越看越懵,更别说什么高阶循环算法了,看着别人写的简洁循环代码,自己却只能写一堆冗余语句?
其实真不用慌!循环是Python的“基础灵魂”,也是小白入门必过的一关——它就像工厂里的流水线,重复做同一件事,省时间、省力气,学会了就能轻松搞定80%的重复操作需求。
今天这篇文章,专门为小白量身打造,所有循环体、难点语法、高阶算法全覆盖,每一个知识点都配「通俗白话+实战例题+应用场景」,不用死记硬背,跟着敲一遍代码,就能彻底吃透循环!
话不多说,直接上干货,建议收藏+转发,避免下次找不到~
一、入门必学:3种基础循环体(小白先吃透这部分!)
Python里最基础的循环就3种:for循环、while循环、嵌套循环。其中for循环最常用,while循环最灵活,嵌套循环是进阶基础,咱们一个个来,不跳步、不绕弯。
1. for循环:“遍历高手”,已知次数优先用
白话解释:就像你挨个吃一串葡萄,从第一颗吃到最后一颗,每一颗都吃一遍,吃完就结束——for循环就是“逐个遍历”可迭代对象(比如列表、字符串、数字序列),遍历完所有元素,循环就终止。
核心语法(难点标注):
for 变量名 in 可迭代对象: 循环体代码(必须缩进!小白最容易踩的坑)# 补充:可迭代对象 = 能逐个取出元素的东西(列表、字符串、range()等)
基础例题1:遍历列表(最常用场景)
需求:打印列表里所有的水果名称,逐个输出。
# 定义一个水果列表fruits = ["苹果", "香蕉", "橙子", "葡萄"]# for循环遍历for fruit in fruits: print("我爱吃:", fruit)# 运行结果:# 我爱吃: 苹果# 我爱吃: 香蕉# 我爱吃: 橙子# 我爱吃: 葡萄
难点解析:变量名(这里的fruit)可以自定义,比如叫a、b、name都可以,只要符合Python变量命名规则(不能数字开头、不能用关键字);循环体必须缩进(一般4个空格),否则会报错(小白高频错误!)。
基础例题2:结合range()函数(控制循环次数)
需求:打印1-10的所有数字,计算1-10的总和。
# 1. 打印1-10(range(1,11)表示从1到10,左闭右开,小白必记!)for i in range(1, 11): print(i, end=" ") # end=" " 表示不换行,横向输出# 运行结果:1 2 3 4 5 6 7 8 9 10# 2. 计算1-10的总和(实战常用)total = 0 # 初始化总和变量,小白容易忘记这一步!for i in range(1, 11): total += i # 等价于 total = total + iprint("\n1-10的总和:", total)# 运行结果:1-10的总和: 55
应用场景:遍历数据、批量处理(比如批量修改文件名、批量打印内容)、固定次数的重复操作,优先用for循环,简洁又不易出错。
2. while循环:“条件王者”,未知次数用它准没错
白话解释:就像你一直喝水,直到不渴为止——while循环只看“条件”,只要条件成立,就一直执行循环体;条件不成立,循环就终止。它适合“不知道要循环多少次”的场景。
核心语法(难点标注):
while 条件表达式: 循环体代码(同样要缩进!) (可选)更新条件的代码(小白最容易漏,导致死循环!)
基础例题1:简单条件循环(打印1-5)
# 初始化变量(控制循环次数)i = 1# 条件:i <= 5 成立,就执行循环while i <= 5: print(i) i += 1 # 关键!更新i的值,否则i一直是1,循环永远不结束(死循环)# 运行结果:# 1# 2# 3# 4# 5
难点解析:死循环是小白用while循环的最大坑!比如上面的代码,如果去掉i += 1,i永远是1,条件i <=5一直成立,程序会一直打印1,直到强制关闭。记住:while循环一定要有“让条件最终不成立”的代码(比如i += 1、i -= 1)。
基础例题2:实战场景(猜数字游戏)
需求:随机生成一个1-100的数字,让用户猜,直到猜对为止,提示“猜大了”“猜小了”。
import random # 导入随机数模块target = random.randint(1, 100) # 生成1-100的随机数guess = 0 # 初始化用户猜的数字# 条件:用户猜的数字 != 目标数字,就一直循环while guess != target: guess = int(input("请猜一个1-100的数字:")) if guess > target: print("猜大了!再试试~") elif guess < target: print("猜小了!再试试~")print("恭喜你,猜对啦!")
应用场景:用户交互(猜数字、登录验证)、实时监控(比如监控某个数值,直到达到阈值)、未知次数的重复操作,用while循环更灵活。
3. 嵌套循环:“循环套循环”,处理多维数据
白话解释:就像你整理书架,先遍历每一层(外层循环),再遍历每一层的每一本书(内层循环)——嵌套循环就是“循环里面再套一个循环”,外层循环执行一次,内层循环执行完所有次数。
核心语法:
# for嵌套for(最常用)for 外层变量 in 外层可迭代对象: for 内层变量 in 内层可迭代对象: 内层循环体 外层循环体(可选)# while嵌套while(较少用)while 外层条件: while 内层条件: 内层循环体 外层循环体(更新外层条件)
基础例题1:打印九九乘法表(嵌套循环经典案例)
# 外层循环控制行数(1-9)for i in range(1, 10): # 内层循环控制每行的列数(1到当前行数i) for j in range(1, i+1): # 打印乘法表达式,不换行 print(f"{j}×{i}={i*j}", end="\t") # 外层循环每执行一次,换行一次 print()# 运行结果就是标准的九九乘法表
难点解析:内层循环的范围要和外层循环关联(比如这里j的范围是1到i+1),否则会出现多余的内容;缩进要分清(外层循环体缩进4格,内层再缩进4格),小白容易把缩进弄混,导致语法错误。
基础例题2:遍历二维列表(实战常用)
# 定义一个二维列表(列表里面套列表,模拟表格数据)students = [ ["小明", 90, 85], ["小红", 88, 92], ["小刚", 78, 80]]# 外层循环遍历每一个学生(每一行)for student in students: # 内层循环遍历每个学生的信息(每一列) for info in student: print(info, end=" ") print() # 换行# 运行结果:# 小明 90 85 # 小红 88 92 # 小刚 78 80
应用场景:处理二维数据(表格、矩阵)、打印图形(三角形、矩形)、批量处理多层级数据(比如遍历文件夹里的所有文件)。
二、小白必避:循环难点语法(这些坑90%的人都踩过)
学会了基础循环,还要掌握3个核心难点语法:break、continue、else,这三个语法能让你更灵活地控制循环,避免冗余代码,同时也是面试高频考点。
1. break:“强制终止”,直接跳出整个循环
白话解释:就像你吃葡萄,吃到一颗坏的,直接停止吃所有葡萄——break一旦执行,不管循环条件是否成立,都会立即跳出当前所在的循环(嵌套循环中,只跳出内层循环)。
例题:查找列表中的第一个偶数,找到就停止
numbers = [1, 3, 5, 4, 7, 8]for num in numbers: if num % 2 == 0: # 判断是否是偶数 print(f"找到第一个偶数:{num}") break # 找到后,直接跳出循环,不再继续遍历 print(f"当前数字:{num}(不是偶数)")# 运行结果:# 当前数字:1(不是偶数)# 当前数字:3(不是偶数)# 当前数字:5(不是偶数)# 找到第一个偶数:4
易错点:break只跳出“当前所在的循环”,如果是嵌套循环,不会跳出外层循环。比如上面的代码,如果是嵌套循环,break只会跳出内层,外层还会继续执行。
2. continue:“跳过当前”,不终止整个循环
白话解释:就像你吃葡萄,吃到一颗酸的,跳过这一颗,继续吃后面的——continue执行后,会跳过当前迭代的剩余代码,直接进入下一次循环,不会终止整个循环。
例题:打印1-10中的奇数,跳过偶数
for i in range(1, 11): if i % 2 == 0: # 判断是否是偶数 continue # 跳过当前偶数,直接进入下一次循环 print(i) # 只打印奇数# 运行结果:1 3 5 7 9
易错点:continue和break的区别——break是“终止整个循环”,continue是“跳过当前迭代”。小白容易搞混,记住:continue之后,循环还会继续,只是跳过当前这一次。
3. 循环else:“循环正常结束”才执行
白话解释:就像你吃完一串葡萄,没有吃到坏的,最后说一句“这串葡萄真甜”——else和for/while循环搭配,只有当循环“正常结束”(没有被break终止)时,才会执行else里的代码。
例题:判断列表中是否有偶数,没有则提示“无偶数”
numbers = [1, 3, 5, 7, 9]for num in numbers: if num % 2 == 0: print(f"找到偶数:{num}") breakelse: # 只有当循环没有被break终止(即没有找到偶数),才会执行这里 print("列表中没有偶数")# 运行结果:列表中没有偶数
易错点:else是和“循环”搭配,不是和if搭配!小白容易误以为else是if的else,其实不是——只要循环正常结束(没有break),不管if条件是否成立,else都会执行。
三、进阶提升:循环高阶算法(白话解析+实战代码)
小白吃透基础循环后,再掌握这3种高阶循环算法,就能轻松应对复杂场景,代码简洁又高效,还能惊艳面试官!每一种算法都用白话讲透,不用懂复杂的编程术语。
1. 列表推导式:“一行搞定循环”,简洁高效
白话解释:就像你用流水线一次性生产一批产品,而不是一个个手动制作——列表推导式是for循环的“简化版”,用一行代码就能完成“遍历+处理+生成新列表”,比普通for循环更简洁、运行速度更快。
核心语法:
新列表 = [处理后的变量 for 变量 in 可迭代对象 if 条件(可选)]
例题1:用列表推导式生成1-10的平方列表(替代普通for循环)
# 普通for循环写法(3行代码)square_list = []for i in range(1, 11): square_list.append(i ** 2)print(square_list) # 结果:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]# 列表推导式写法(1行代码)square_list = [i ** 2 for i in range(1, 11)]print(square_list) # 结果和上面一样
例题2:带条件的列表推导式(筛选偶数的平方)
# 需求:生成1-10中偶数的平方列表even_square = [i ** 2 for i in range(1, 11) if i % 2 == 0]print(even_square) # 结果:[4, 16, 36, 64, 100]# 等价于普通for循环+if判断(4行代码)even_square = []for i in range(1, 11): if i % 2 == 0: even_square.append(i ** 2)
应用场景:快速生成列表、筛选数据、简单数据处理,用列表推导式能大幅简化代码,提升效率。
2. 迭代器(Iterator):“按需取值”,节省内存
白话解释:就像你去超市买东西,不需要把所有东西都搬回家,而是买一个拿一个——迭代器是一种“按需取值”的循环方式,不会一次性把所有数据加载到内存中,适合处理大数据(比如100万条数据)。
核心知识点(白话版):
可迭代对象(比如列表、字符串)可以通过iter()函数变成迭代器;
迭代器通过next()函数“逐个取值”,取完最后一个值后,会报错(StopIteration);
for循环的底层原理,就是把可迭代对象变成迭代器,然后不断调用next()取值,直到报错为止(只是Python自动处理了报错,我们看不到)。
例题:用迭代器遍历列表(模拟for循环底层)
# 1. 定义一个列表(可迭代对象)fruits = ["苹果", "香蕉", "橙子"]# 2. 把可迭代对象变成迭代器fruit_iter = iter(fruits)# 3. 用next()逐个取值print(next(fruit_iter)) # 结果:苹果print(next(fruit_iter)) # 结果:香蕉print(next(fruit_iter)) # 结果:橙子# print(next(fruit_iter)) # 取完所有值,报错:StopIteration# 4. 用for循环遍历迭代器(更简洁,Python自动处理报错)for fruit in fruit_iter: print(fruit)
应用场景:处理大数据(比如读取100万行的文件)、节省内存,避免一次性加载所有数据导致程序卡顿。
3. 生成器(Generator):“迭代器的升级版”,更灵活
白话解释:就像你自己做流水线,需要的时候才生产产品,生产一个就输出一个——生成器是一种特殊的迭代器,不用手动调用iter()和next(),用yield关键字定义,每次执行到yield就暂停,返回一个值,下次继续执行。
核心语法:
def 生成器函数名(): for 变量 in 可迭代对象: yield 处理后的变量 # 每次返回一个值,暂停循环
例题:生成1-10的平方(用生成器,节省内存)
# 定义生成器函数def square_generator(): for i in range(1, 11): yield i ** 2 # 每次返回一个平方值,暂停循环# 调用生成器(不会立即执行,只会返回一个生成器对象)square_gen = square_generator()# 遍历生成器(按需取值)for num in square_gen: print(num) # 结果:1 4 9 16 25 36 49 64 81 100# 也可以用next()逐个取值# print(next(square_gen)) # 1# print(next(square_gen)) # 4
难点解析:yield和return的区别——return会终止函数,返回一个值;yield会暂停函数,返回一个值,下次调用时从暂停的地方继续执行。生成器比列表推导式更节省内存,因为它不会一次性生成所有数据,而是按需生成。
应用场景:处理大数据、实时生成数据(比如实时监控日志)、节省内存的场景,比迭代器更灵活、更易编写。
4. 杨辉三角(循环经典应用,面试高频)
白话解释:就像搭积木,每一行的数字都是由上一行相邻两个数字相加得到的,最外层的数字永远是1——用嵌套循环就能轻松实现,外层循环控制行数,内层循环控制每一行的数字,核心是“上一行推导下一行”。
核心思路:先初始化第一行(只有1),之后每一行的第一个数和最后一个数都是1,中间的数 = 上一行对应位置的前一个数 + 上一行对应位置的数,用循环逐行生成。
例题:打印10行杨辉三角(带运行结果)
# 定义杨辉三角的行数rows = 10# 初始化第一行(只有1个元素)yanghui = [[1]]# 外层循环控制行数(从第2行到第10行,索引从1开始)for i in range(1, rows): # 初始化当前行,第一个元素是1 current_row = [1] # 内层循环控制当前行的中间元素(从第2个到倒数第2个) for j in range(1, i): # 中间元素 = 上一行的前一个元素 + 上一行的当前元素 current_num = yanghui[i-1][j-1] + yanghui[i-1][j] current_row.append(current_num) # 当前行的最后一个元素是1 current_row.append(1) # 将当前行添加到杨辉三角中 yanghui.append(current_row)# 打印杨辉三角(格式化输出,更美观)for row in yanghui: # 居中对齐,每行前面加空格,更像三角 print(" " * (rows - len(row)), end="") for num in row: print(f"{num:^4}", end="") # 每个数字占4位,居中显示 print() # 换行# 运行结果(10行杨辉三角):# 1 # 1 1 # 1 2 1 # 1 3 3 1 # 1 4 6 4 1 # 1 5 10 10 5 1 # 1 6 15 20 15 6 1 # 1 7 21 35 35 21 7 1 # 1 8 28 56 70 56 28 8 1 # 1 9 36 84 126 126 84 36 9 1
难点解析:核心是“上一行推导下一行”,小白容易混淆内层循环的范围(j从1到i,因为每行的中间元素个数 = 行数-2);格式化输出时,用空格居中,让结果更直观,避免打印成“列表形式”。
5. 冒泡排序(循环排序算法,入门必学)
白话解释:就像水里的气泡,大的气泡先浮上来——每次循环都从列表开头开始,逐个比较相邻的两个元素,把大的元素“往后推”,经过一轮循环,最大的元素就会跑到列表最后;重复这个过程,直到所有元素都排好序。
核心思路:外层循环控制排序轮数(最多需要列表长度-1轮),内层循环控制每一轮的比较次数(每轮比上一轮少1次,因为最后一个元素已经排好序),相邻元素比较,不符合顺序就交换位置。
例题:用冒泡排序对列表[3, 1, 4, 1, 5, 9, 2, 6]排序(带运行结果)
# 定义需要排序的列表nums = [3, 1, 4, 1, 5, 9, 2, 6]# 列表长度(用于控制循环次数)n = len(nums)# 外层循环:控制排序轮数(n-1轮即可,最后一个元素无需排序)for i in range(n - 1): # 内层循环:控制每一轮的比较次数(每轮比上一轮少1次) for j in range(n - 1 - i): # 比较相邻两个元素,前一个比后一个大,就交换位置 if nums[j] > nums[j + 1]: nums[j], nums[j + 1] = nums[j + 1], nums[j] # 可选:打印每一轮排序后的结果,查看排序过程 print(f"第{i+1}轮排序后:{nums}")# 打印最终排序结果print("最终排序结果:", nums)# 运行结果:# 第1轮排序后:[1, 3, 1, 4, 5, 2, 6, 9]# 第2轮排序后:[1, 1, 3, 4, 2, 5, 6, 9]# 第3轮排序后:[1, 1, 3, 2, 4, 5, 6, 9]# 第4轮排序后:[1, 1, 2, 3, 4, 5, 6, 9]# 第5轮排序后:[1, 1, 2, 3, 4, 5, 6, 9]# 第6轮排序后:[1, 1, 2, 3, 4, 5, 6, 9]# 第7轮排序后:[1, 1, 2, 3, 4, 5, 6, 9]# 最终排序结果: [1, 1, 2, 3, 4, 5, 6, 9]
难点解析:小白容易忘记“内层循环的次数 = n-1-i”,导致重复比较已排好序的元素;可以添加“优化判断”(如果某一轮没有交换元素,说明已经排好序,直接终止循环),提升效率(示例中第4轮后已排好序,后续轮次无交换)。
6. 质数查找(循环判断算法,实战常用)
白话解释:质数就是“只能被1和它本身整除的大于1的整数”——用循环判断一个数是否能被2到它的平方根之间的任何数整除,如果不能,就是质数;批量查找质数时,用外层循环遍历范围,内层循环进行判断。
核心思路:判断一个数n是否为质数,只需判断它能否被2到√n之间的数整除(因为大于√n的因数,必然和小于√n的因数成对出现,无需重复判断),能整除则不是质数,不能则是质数。
例题:查找1-50之间的所有质数(带运行结果)
import math # 导入math模块,用于计算平方根primes = [] # 存储找到的质数# 外层循环:遍历1-50的所有数for n in range(2, 51): # 质数从2开始,1不是质数 is_prime = True # 标记是否为质数,默认是 # 内层循环:判断n能否被2到√n之间的数整除 for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: # 能整除,说明不是质数 is_prime = False break # 无需继续判断,跳出内层循环 if is_prime: # 如果是质数,添加到列表中 primes.append(n)# 打印1-50之间的所有质数print("1-50之间的质数:", primes)# 打印质数的个数print(f"1-50之间共有{len(primes)}个质数")# 运行结果:# 1-50之间的质数: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]# 1-50之间共有15个质数
难点解析:小白容易忽略“质数从2开始”,把1也算作质数;另外,内层循环的范围是“2到√n”,用math.sqrt(n)计算平方根后,要转成整数并加1(因为range是左闭右开),否则会漏掉边界值的判断。
7. 高端算法:循环+动态规划(斐波那契数列优化,面试加分)
白话解释:斐波那契数列就是“前两个数是1,后面每个数都是前两个数的和”(1,1,2,3,5,8...),普通递归会重复计算很多次,用“循环+动态规划”的思路,把已经计算过的数存起来,避免重复计算,效率大幅提升——这是高端循环算法的核心思路:“用空间换时间”。
核心思路:初始化前两个斐波那契数,然后用循环从第3个数开始,每个数都等于前两个数的和,同时把计算结果存到列表中,后续直接调用已存的结果,无需重复计算,适合计算大规模斐波那契数。
例题:用循环+动态规划计算前20个斐波那契数(带运行结果)
# 定义要计算的斐波那契数列个数n = 20# 初始化动态规划列表,存储已计算的斐波那契数fib = [0] * n# 前两个斐波那契数都是1fib[0] = 1fib[1] = 1# 循环计算从第3个到第20个斐波那契数(索引从2开始)for i in range(2, n): # 第i个数 = 第i-1个数 + 第i-2个数(调用已存的结果,无需重复计算) fib[i] = fib[i-1] + fib[i-2]# 打印前20个斐波那契数print(f"前{20}个斐波那契数:", fib)# 打印第20个斐波那契数print(f"第{20}个斐波那契数:", fib[-1])# 运行结果:# 前20个斐波那契数: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]# 第20个斐波那契数: 6765
8. 高端算法:最长公共子序列(LCS),面试高频考点
白话解释:就像找两个字符串里“共同的、不要求连续但顺序不变的字符序列”——比如“abcde”和“ace”,最长公共子序列就是“ace”;核心用“循环+动态规划”,把两个字符串的匹配情况拆成小问题,逐步计算,避免重复运算,比暴力循环效率高太多。
核心思路:创建一个二维表格(dp表),dp[i][j]表示“第一个字符串前i个字符”和“第二个字符串前j个字符”的最长公共子序列长度;用双重循环遍历两个字符串,若当前字符相同,就继承上一个匹配的长度+1;若不同,就取“上一行”或“上一列”的最大长度,逐步填充表格,最终表格右下角的数值就是最长长度,再反向推导得到具体子序列。
例题:求两个字符串的最长公共子序列(带运行结果)
def lcs(str1, str2): # 获取两个字符串的长度 m = len(str1) n = len(str2) # 创建dp表,m+1行n+1列,初始值全为0(空字符串的匹配长度为0) dp = [[0]*(n+1) for _ in range(m+1)] # 双重循环,填充dp表(外层遍历str1,内层遍历str2) for i in range(1, m+1): for j in range(1, n+1): # 若当前字符相同,说明找到一个匹配字符,长度+1 if str1[i-1] == str2[j-1]: dp[i][j] = dp[i-1][j-1] + 1 # 若不同,取“上一行”(dp[i-1][j])或“上一列”(dp[i][j-1])的最大值 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) # 反向推导,获取最长公共子序列的具体内容 i, j = m, n lcs_str = [] while i > 0 and j > 0: if str1[i-1] == str2[j-1]: lcs_str.append(str1[i-1]) i -= 1 j -= 1 else: # 往数值大的方向移动 if dp[i-1][j] > dp[i][j-1]: i -= 1 else: j -= 1 # 反向推导得到的是倒序,需反转成正序 lcs_str.reverse() return dp[m][n], ''.join(lcs_str)# 测试案例str1 = "abcde"str2 = "ace"length, result = lcs(str1, str2)print(f"字符串1:{str1}")print(f"字符串2:{str2}")print(f"最长公共子序列长度:{length}")print(f"最长公共子序列:{result}")# 运行结果:# 字符串1:abcde# 字符串2:ace# 最长公共子序列长度:3# 最长公共子序列:ace# 再测试一个复杂案例str3 = "abcbdab"str4 = "bdcaba"length2, result2 = lcs(str3, str4)print(f"\n字符串3:{str3}")print(f"字符串4:{str4}")print(f"最长公共子序列长度:{length2}")print(f"最长公共子序列:{result2}")# 运行结果:# 字符串3:abcbdab# 字符串4:bdcaba# 最长公共子序列长度:4# 最长公共子序列:bcab(或bdab,多个正确答案均可)
难点解析:小白容易混淆dp表的含义(dp[i][j]对应前i、j个字符),记住“字符串索引从0开始,dp表索引从1开始”;反向推导时,重点是“字符相同则记录,不同则往数值大的方向移动”,无需死记公式,理解“拆分成小问题”的思路即可。
9. 质数查找进阶:埃拉托色尼筛法(高效批量找质数)
白话解释:就像“筛沙子”,先把所有待筛选的数字摆好,从最小的质数2开始,把2的所有倍数(4、6、8...)都筛掉;再找下一个没被筛掉的数(3),把3的所有倍数筛掉;重复这个过程,最后剩下的数,全是质数——比普通循环判断质数效率高得多,适合批量查找大范围的质数。
核心思路:初始化一个布尔列表(is_prime),默认所有数都是质数(True),先把0和1标记为非质数(False);再用循环从2开始,遍历到目标数的平方根,若当前数是质数(未被筛掉),就把它的所有倍数(从当前数的平方开始,更高效)标记为非质数,循环结束后,列表中为True的数就是质数。
例题:用埃拉托色尼筛法查找1-100之间的所有质数(带运行结果)
def sieve_of_eratosthenes(n): # 初始化布尔列表,索引代表数字,值代表是否为质数(默认全为True) is_prime = [True] * (n + 1) # 0和1不是质数,标记为False is_prime[0] = is_prime[1] = False # 循环从2开始,遍历到n的平方根(超过平方根的数无需判断) for i in range(2, int(n**0.5) + 1): # 若当前数是质数,就筛掉它的所有倍数 if is_prime[i]: # 从i*i开始筛(小于i*i的倍数,已被更小的质数筛掉,无需重复) is_prime[i*i : n+1 : i] = [False] * len(is_prime[i*i : n+1 : i]) # 收集所有质数(索引为True的数) primes = [i for i, val in enumerate(is_prime) if val] return primes# 测试:查找1-100之间的质数primes_100 = sieve_of_eratosthenes(100)print(f"1-100之间的质数:{primes_100}")print(f"1-100之间共有{len(primes_100)}个质数")# 运行结果:# 1-100之间的质数: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]# 1-100之间共有25个质数
难点解析:小白容易疑惑“为什么从i*i开始筛”,其实是因为小于i*i的倍数(比如i=5,小于25的倍数10、15、20),已经被2、3等更小的质数筛掉了,重复筛会浪费时间;另外,布尔列表的切片赋值(is_prime[i*i : n+1 : i] = [False] * ...)是核心技巧,简化了循环筛除的代码,比逐个标记更高效。
对比普通质数查找:普通方法是“逐个判断每个数是否为质数”,时间复杂度高;埃拉托色尼筛法是“批量筛除非质数”,时间复杂度大幅降低,找10000、100000甚至更大范围的质数时,差距会非常明显,是实战和面试中首选的质数查找算法。
难点解析:动态规划的核心是“缓存已计算结果”,小白容易陷入“递归重复计算”的误区(比如直接用递归计算第20个斐波那契数,会重复计算上千次);用循环+列表缓存,既能保证效率,又容易理解,是高端算法中最适合小白入门的类型。
四、小白实战:循环综合案例(直接套用)
学完所有知识点,一定要动手敲代码!这个综合案例结合了基础循环、难点语法和高阶算法,学会了就能应对大部分循环场景。
案例需求:批量处理学生成绩,统计及格(60分及以上)和不及格的学生,计算及格学生的平均分,用生成器生成及格学生的姓名列表。
# 学生成绩数据(二维列表)students = [ ["小明", 85], ["小红", 92], ["小刚", 58], ["小丽", 76], ["小强", 45], ["小芳", 88]]# 1. 统计及格和不及格人数,计算及格平均分pass_count = 0 # 及格人数fail_count = 0 # 不及格人数pass_total = 0 # 及格总分# 普通for循环遍历for name, score in students: if score >= 60: pass_count += 1 pass_total += score else: fail_count += 1# 计算及格平均分(避免除以0)pass_average = pass_total / pass_count if pass_count > 0 else 0print(f"及格人数:{pass_count},不及格人数:{fail_count}")print(f"及格学生平均分:{pass_average:.1f}") # .1f 表示保留1位小数# 2. 用生成器生成及格学生的姓名列表def pass_student_generator(): for name, score in students: if score >= 60: yield name# 遍历生成器,打印及格学生姓名print("及格学生姓名:", end="")for name in pass_student_generator(): print(name, end=" ")# 运行结果:# 及格人数:4,不及格人数:2# 及格学生平均分:85.2# 及格学生姓名:小明 小红 小丽 小芳
五、小白总结(必看!)
循环其实没有那么难,记住这3个核心点,就能轻松吃透:
基础循环:for(已知次数)、while(未知次数)、嵌套循环(多维数据),缩进是关键;
难点语法:break(终止循环)、continue(跳过当前)、else(循环正常结束执行),分清用法不踩坑;
高阶算法:列表推导式(简洁高效)、迭代器(按需取值)、生成器(灵活省内存),按需选择使用。
最后提醒一句:Python循环的核心是“重复做同一件事”,多动手敲代码,多修改例题(比如把打印1-10改成打印1-100,把猜数字改成猜字母),很快就能熟练掌握。
如果觉得这篇文章对你有帮助,记得点赞、在看、转发给身边学Python的小伙伴~ 关注我,后续更新更多小白友好的Python干货,一起从入门到精通❤️
文末福利:评论区回复“循环”,领取本文所有例题的完整代码+练习题,帮你巩固知识点!