Skip to content

C# - 嵌套循环

一个**嵌套循环(nested loop)**是放置在另一个循环内部的循环。对于外层循环的每一次单独迭代,内层循环都会完成其整个迭代序列。这种结构对于处理二维数据,例如网格、表格或矩阵非常常见。

你可以嵌套任何类型的循环。最常见的组合是一个 for 循环内部嵌套另一个 for 循环。

// 嵌套 `for` 循环语法
for (int i = 0; i < outerMax; i++)
{
// 这是外层循环
for (int j = 0; j < innerMax; j++)
{
// 这是内层循环
// 这里的代码在每次外层循环迭代时执行 innerMax 次
}
}

一个关键点是内层循环的总执行次数是外层循环次数和内层循环次数的乘积(例如,如果外层循环运行 3 次,内层循环运行 4 次,最内层的代码块将执行 3 * 4 = 12 次)。

乘法表是嵌套循环的一个经典且直观的示例。外层循环遍历行(被乘数),内层循环遍历列(乘数)。

using System;
int tableSize = 5;
// 外层循环用于行(1 到 5)
for (int i = 1; i <= tableSize; i++)
{
// 内层循环用于列(1 到 5)
for (int j = 1; j <= tableSize; j++)
{
// {i * j, -4} 将数字填充到 4 个字符以对齐
Console.Write($"{i * j, -4}");
}
// 每行完成后换到下一行
Console.WriteLine();
}

代码生成了一个格式整齐的乘法表:

1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

嵌套循环对于处理二维数组至关重要。外层循环通常遍历行,内层循环遍历每行中的列。

using System;
// 表示简单游戏板的二维数组
char[,] board =
{
{ 'X', 'O', 'X' },
{ 'O', 'X', 'O' },
{ 'O', 'O', 'X' }
};
// 获取行数和列数
int rows = board.GetLength(0);
int cols = board.GetLength(1);
Console.WriteLine("Game Board:");
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Console.Write($"{board[i, j]} ");
}
Console.WriteLine();
}
Game Board:
X O X
O X O
O O X

我们可以使用嵌套循环查找给定范围内的素数。如果一个数字只能被 1 和自身整除,那么它就是素数。外层循环遍历要检查的数字,内层循环检查因子。

using System;
Console.WriteLine("Prime numbers between 2 and 100:");
// 外层循环:遍历每个要测试的数字
for (int numberToTest = 2; numberToTest <= 100; numberToTest++)
{
bool isPrime = true;
// 内层循环:检查从 2 到数字平方根的因子。
// 这是一个优化:如果一个数字有一个大于其平方根的因子,
// 它也必定有一个更小的因子。
for (int factor = 2; factor * factor <= numberToTest; factor++)
{
if (numberToTest % factor == 0)
{
isPrime = false;
break; // 找到因子,不是素数。退出内层循环。
}
}
if (isPrime)
{
Console.Write($"{numberToTest} ");
}
}
Console.WriteLine();

上述代码生成了一个素数列表:

Prime numbers between 2 and 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
  • 注意复杂性: 如果集合很大,嵌套循环可能导致性能不佳。对 n 个项进行嵌套循环的时间复杂度通常为 O(n²)(O-n-平方)。这意味着如果输入大小加倍,执行时间大约会增加四倍。
  • 避免深度嵌套: 包含三层或更多层嵌套循环的代码会变得非常难以阅读和调试。如果你发现自己需要深度嵌套,请考虑将代码重构为更小、更专业的方法。
  • 使用 break 进行优化: 如素数示例所示,一旦获得所需结果,就使用 break 退出内层循环。这可以防止许多不必要的迭代。
  • 考虑替代方案: 对于某些复杂的数据处理任务,使用 LINQ(语言集成查询)可以提供比手动嵌套循环更具可读性和声明性的替代方案。