CC++ & Algorithm

你以为在遍历列表,其实你在“排座位”——嵌套列表的底层逻辑

你有没有过这样的时刻:写了一个 for 循环,自信满满地打印嵌套列表,结果输出了一堆 ['小明', '小红'] 这样的“半成品”?你明明写了循环,为什么拿不到里面的名字?

因为你只剥开了一层洋葱。嵌套列表,本质上就是一个“列表里装着列表”的结构,像教室里的座位表:外层是排,内层是每排的同学。要找到每一个具体的名字,必须两层循环——一层用来“找到哪一排”,另一层用来“找到这一排里的谁”。

先从座位表说起

假设教室有 3 排,每排 4 个同学:

seats = [
    ["小明", "小红", "小刚", "小美"],
    ["小华", "小丽", "小强", "小芳"],
    ["小军", "小智", "小文", "小杰"]
]

seats 的外层列表有三个元素,每个元素本身又是一个列表。如果你想打印所有名字,最直观的方式是:

for row in seats:
    for name in row:
        print(name, end=" ")
    print()

外层的 row 是“一排人”,内层的 name 才是“具体的人”。少了任何一层,你都只能拿到“一排人”的整体,而不是里面的个体。

这听起来很简单,但真到了写代码的时候,很多人会犯一个典型的错误:只写一层循环,打印 row,然后疑惑“为什么输出的是列表?”——因为你手里拿到的是一整排的名册,而不是名册上的某个名字。

有时候你还想知道“第几排第几个”

单纯打印名字还不够。比如你要发作业本,得喊“第 2 排第 3 个同学,这是你的本子”。这时候需要同时知道索引和值,Python 给了我们 enumerate

for i, row in enumerate(seats, start=1):
    for j, name in enumerate(row, start=1):
        print(f"第{i}排第{j}个是{name}")

start=1 是为了让排号和座位号从 1 开始,符合人类的直觉。如果你用默认的 0,就会输出“第 0 排第 0 个”,那会让班主任疯掉的。

这里有一个容易翻车的细节:内外层循环变量名不能相同。有人写:

for row in seats:
    for row in row:  # 内层 row 覆盖了外层 row
        print(row)

内层循环一执行,row 就变成了最后一个名字,外层循环直接崩溃。变量名不仅仅是“名字”,它代表你当前正在操作的“那一层数据”。内层变量叫 name,外层叫 row,各司其职,互不干扰。

嵌套列表不只是座位表,还是矩阵

你以为嵌套列表只是用来存名字的?太小看它了。矩阵,就是嵌套列表的“学术形态”。

例如,让你计算两个矩阵的和。输入两个 n 行 m 列的矩阵 A 和 B,输出 A+B。考察的就是你能不能把两个嵌套列表的对应元素相加:

for i in range(n):
    for j in range(m):
        C[i][j] = A[i][j] + B[i][j]

外层循环控制行,内层循环控制列,A[i][j]B[i][j] 就是同一位置上的两个数。这个逻辑,和“找到座位表里第 i 排第 j 个同学”一模一样。

矩阵乘法稍微复杂一点,但本质也没变:

for i in range(n):
    for j in range(k):
        for t in range(m):
            C[i][j] += A[i][t] * B[t][j]

三层循环,每一层对应一个维度:i 是 A 的行,j 是 B 的列,t 是求和的下标。如果你能驾驭“排座位”的两层循环,那么三层循环只是“多套一层”而已,就像教室里有 3 个年级,每个年级 3 个班,每个班 4 排座位——你想找到某个同学,就得一层一层往下钻。

常见坑:少一层、变量混用、越界

我见过太多人栽在这三个坑里。

第一个坑:只写一层循环。比如有题目问,classroom = [["小明","小红"],["小刚","小丽"],["小华","小强"]],要依次输出每个名字。有人写:

for name in classroom:
    print(name)

输出的是 ["小明", "小红"] 这种列表,而不是“小明”。因为 name 这个变量名误导了你——它实际指向的是子列表,而不是名字。变量叫什么不重要,重要的是它对应哪一层数据。

第二个坑:内外层变量名重复。前面已经说过了,这是新手最容易犯的错。

第三个坑:用下标时越界。比如有人用 range(len(matrix[i]) + 1),多了一个,直接 IndexError。如果你非要用下标,记住内层循环的范围是 len(matrix[i]),不是 len(matrix[i]) + 1,更不是某个拍脑袋的数。

一个判断:下面这段代码能正确打印吗?

matrix = [[1,2],[3,4],[5,6]]
for i in range(len(matrix)):
    for j in range(len(matrix[i])):
        print(matrix[i][j])

能。外层 i 遍历的是三个子列表的索引,内层 j 遍历的是每个子列表内部元素的索引。matrix[i][j] 正好访问到每一个数字。即使子列表长度不一样,len(matrix[i]) 也能保证不越界——因为每个子列表的长度都是它自己的。这正是安全遍历的关键:用当前子列表的实际长度来决定内层循环的范围

从二维到任意维,你需要的只是“再多一层”

学完嵌套列表的遍历,你会发现自己能做的事情忽然变多了:课程表(星期×节次)、游戏地图(行×列)、成绩表(学生×科目)……本质上都是二维数据,都可以用两层循环搞定。

如果你再进一步,遇到三维数据(比如“每个年级每个班每个学生”),只需要再多套一层循环。循环的层数和嵌套的深度一一对应,这个规律不会变。

所以我给你的建议是:别怕嵌套。把它想象成“剥洋葱”——每剥一层,你就离核心更近一步。写代码的时候,先理清楚有几层结构,再决定写几个循环。一层结构用一个循环,两层结构用两个循环,永远不要试图用一个循环去硬闯两层数据,那样只会撞得满头包。


关于作者

我是赵老师,持有 NOI 信息学奥赛教练证书,拥有 15 年以上的软件开发经验,从事信息学少儿编程教学已有 8 年时间。

这些年累计帮助 多名 学生通过编程特长升入自己心仪的目标学校。

如果你在编程学习上有任何疑问,欢迎联系我:18620372957(微信同号)

这篇文章对你有帮助吗?

成为第一个评价的人

评论0

还没有评论,来抢沙发~

评论加载中...

想系统学习这个知识点?查看完整知识点 →