嵌套循环

上一节的所有示例代码中我们定义的都是一维数组,一维数组顾名思义就是将所有的”小盒子“排成一排,用一个下标去索引它们。C/C++ 中可以定义各种维度的数组,比如你可以定义一个二维数组来存储方阵:

#include <bits/stdc++.h>

int a[100][100];

int main ()
{
    a[0][0] = 1; // 二维数组的每个维度都是从0开始编号的!
    a[2][3] = 5;
    std::cout << a[0][0] << '\n'; //输出结果为1
    return 0;
}

有了多维数组后,你会发现我们手里原有的线性的循环工具有点不够用了。但事实上我们可以用嵌套循环来轻松地对多维数组进行访问。下面的示例程序接受一个 $n$ 行 $m$ 列的包含整数的方阵作为输入,它会将其原封不动地打印出来 (假设 $n,m\leq 100$):

#include <bits/stdc++.h>

int a[100][100];

int main ()
{
    int row, col;
    std::cin >> row >> col;

    // input
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            std::cin >> a[i][j];
        }
    }

    // output
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
        {
            char suffix_char = ((j == col - 1) ? '\n' : ' ');
            std::cout << a[i][j] << suffix_char;
        }
}

从逻辑上来看嵌套循环也没什么“了不起”:以输出部分为例,对于每一个 $i=0,1,\cdots,row-1$,循环变量 $j$ 都会从 $0$ 循环到 $col-1$,这样在内层循环的里面我们就可以按照 $(0,0)$, $(0,1)$,$\cdots$, $(0, col-1)$, $(1, 0)$, $(1, 1)$, $\cdots$ ,$(1, col-1)$, $(2, 0)$,$\cdots$, $(row-1, 0)$, $\cdots$, $(row-1, col-1)$ 的顺序去访问二维数组中的每个元素。

这个代码中有一些细节值得讲解:

  • 在书写嵌套循环时,内层循环和外层循环应当使用不同的循环变量。如果你在内层循环中使用了和外层循环一样的变量,那么执行完内层循环后回到外层循环时,变量的值就乱了。不要小瞧这条看上去显而易见的结论,你们所有人大抵都会在过去、现在或将来犯一些类似下面的错误:
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; i++)
    
  • 我们在给出两段双重循环的代码时特地使用了不同的括号风格,你可以借此机会对到底什么时候需要打大括号有更深入的理解:我们之前提到如果当前结构内部只有一条语句,for循环/if语句可以不打大括号;但更准确地说应该是如果只有“一块”一句,比如第二段双重循环的外层循环内部只有一个for循环这“一块”语句,所以可以省略大括号。还是那句话,如果你拿捏不准,把大括号写全永远是最稳妥的。
  • 示例代码中出现了一个比较奇怪的语句:suffix_char = ((j == col - 1) ? '\n' : ' ')? : 被称为三目运算符,是 C/C++ 提供的一个语法机制。(a ? b : c) 的意思是如果 a 的值为真则返回 b,否则返回 c。上面的这一行代码等价于一个if语句:
    if (j == col - 1)
        suffix_char = '\n';
    else
        suffix_char = ' ';
    
    可以看到合理使用三目运算符有助于使代码更加简洁。

运算符优先级

你可能会疑惑:三目运算符的代码写成

suffix_char = (j == col - 1 ? '\n' : ' ');

甚至是

suffix_char = j == col - 1 ? '\n' : ' ';

可不可以呢? 在坐等答案之前,你应该做的事情是把括号去掉并尝试运行程序,这是你自己通过试验寻求问题答案的途径。当然在这里我们会告诉你:上面两段代码是没有问题的,因为 C/C++ 中不同的符号存在优先级差异。正如算术中乘除法比加减法优先级高,C/C++内部也有一套严格的优先级金字塔。优先级相同的运算符优先计算左侧的;优先级不同的运算符优先计算等级高的。这个链接 详细讲述了优先级的划分。

下面展示一个更精巧的例子,它的作用是打印出一个字符三角形:

#include <bits/stdc++.h>

int main ()
{
    int n;
    std::cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < i; j++)
            std:: cout << '*';
        std::cout << '\n';
    }
    return 0;
}

可以看到外层的循环变量是可以在内层循环体和循环条件中使用的。

Previous
Next