CC++ & Algorithm

嵌套的奥秘:为什么两个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开始。

这不是简单的星号替换,它多了一个“循环字母表”的约束。考察的其实是两点:

  1. 行与列的关系依然不变 —— 第 row 行有 row 个字符。
  2. 但字符不再是星号,而是根据一个全局递增计数器的值,对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(微信同号)

这篇文章对你有帮助吗?

成为第一个评价的人

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