嵌套的奥秘:为什么两个for循环能画出整个世界?
你有没有注意到,当你用铅笔在方格纸上画一个正方形时,你的手其实在做两件事:先画一行(从左到右),然后换到下一行再重复同样的动作。这跟程序里的循环嵌套一模一样——只不过电脑不会累,它乐意替你重复千遍万遍。
最直观的例子就是打印一个4×4的星星正方形:
for (int row = 1; row <= 4; row++) {
for (int col = 1; col <= 4; col++) {
cout << "* ";
}
cout << endl;
}
外循环管行数,内循环管列数。内循环跑完一整圈才给外循环交差,外循环才往前走一步。这个配合就像组队打怪:内循环是小兵,负责清完这一层的所有敌人;外循环是队长,一层清完就喊“下一层”。
明白了这个基本模式,你会发现所有图形打印题都在问同一个问题:内循环的次数跟行号有什么关系?
从直角三角到“字母蛇”
最简单的变形:第一行1个星,第二行2个,第三行3个。关系直接到几乎不用想——第 row 行就打印 row 个。内循环条件改成 col <= row 即可。
但有一道题把它变了个有意思的玩法:输入一个n,打印一个由大写字母组成的直角三角形,第三行3个字母,依次从A填到Z,填完Z后又从A开始。
这不是简单的星号替换,它多了一个“循环字母表”的约束。考察的其实是两点:
- 行与列的关系依然不变 —— 第
row行有row个字符。 - 但字符不再是星号,而是根据一个全局递增计数器的值,对26取模后映射到大写字母。
关键代码就几行:
int cnt = 0; // 记录当前是第几个字母
for (int row = 1; row <= n; row++) {
for (int col = 1; col <= row; col++) {
cout << char('A' + (cnt % 26));
cnt++;
}
cout << endl;
}
这里 cnt % 26 就是经典的表盘取模——钟表上12点转完又回到1点,字母表转完Z也回到A。如果用 'A' + 25 得到 'Z',再加1就超出范围,所以必须取模才能实现循环。
这道题本质上是在说:外循环控制行,内循环控制列,但每打印一个字符都可能需要更新一个全局状态。这比单纯打印星形多了一个“状态管理”的层次,面试里常见的“蛇形填数”“螺旋矩阵”就是这类思路的升级版。
常见的坑:少一个“-1”就歪了
另一个常考的变形是:打印直角在右下角的三角形。很多人直接写 j <= i 结果得到的是左下角的三角形。想让它右对齐,得先用空格占位:
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n - i; j++) cout << ' ';
for (int k = 1; k <= i; k++) cout << '*';
cout << endl;
}
这里的 n - i 就是行号与总行数的差值,每一行要补的空格数正好递减到0。这个公式一旦记住,无论画菱形、沙漏还是圣诞树,核心逻辑都一样——先确定每一行星号的数量与行号的函数关系,再确定空格数量的函数关系,两个内循环搞定。
比如菱形,上半部分第i行(从0开始)的空格数是 (n-1)/2 - i,星号数是 2 * i + 1;下半部分反过来。只要把上下分段处理,就没什么神秘。
有一道选择题直接问你内循环条件:
for(int i=1; i<=5; i++) {
for(int j=1; j<=___; j++) {
cout << "*";
}
cout << endl;
}
很显然,直角在左下角时填 i,直角在左上角时填 5 - i + 1(或 6 - i),如果题目描述说是“直角在左下角”,答案就是 i。这种题考察的只是一句话:内循环次数 = 当前行需要打印的个数。
结束之前的思考
循环嵌套就像搭积木,外循环决定你要搭多少层,内循环决定每一层摆几块。真正难的是从图形描述里提取出“第i行有多少个元素”的数学关系。这个关系一旦找对,代码就是一行条件的事。
进阶建议:不要停留在星星和字母上,试着用循环嵌套打印九九乘法表、杨辉三角,甚至用 % 和 / 配合打印模版图案(如数字螺旋)。你会发现,所有二维矩阵遍历、网格绘制、甚至游戏里的地图生成,骨子里都是这一套东西。
当你面对任何一个需要按行按列输出的问题时,先问自己三个问题:
- 外循环从几到几?
- 内循环的上下界跟外循环变量有什么函数关系?
- 每次输出是否需要额外状态(如计数器、空格、换行符)?
答案就在这三个问题里。
关于作者
我是赵老师,持有 NOI 信息学奥赛教练证书,拥有 15 年以上的软件开发经验,从事信息学少儿编程教学已有 8 年时间。
这些年累计帮助 多名 学生通过编程特长升入自己心仪的目标学校。
如果你在编程学习上有任何疑问,欢迎联系我:18620372957(微信同号)