1. 前言
先來問個問題:為什麼我們要把一個迴圈放進另一個迴圈裡?其實很多時候,我們的資料或任務不是單純一條線,而是像表格、網格,甚至多維結構。假設你想印出乘法表、走訪二維陣列,或是計算所有元素對的交集,這時候一個迴圈就不夠用了——你需要巢狀迴圈。
在程式裡,巢狀迴圈就像兩個鬧鐘:外面的開始響,裡面那個每次都會響,直到外面的還在響。也就是說,每次外層「迭代」時,內層都會完整跑一遍(而且每次外層都會再來一次)。
一個很棒的例子就是時鐘和分鐘。時鐘是外層迴圈,從 0 到 23,分鐘是內層,從 0 到 59。每當外層變動一次,內層都會跑完所有值。
2. 巢狀迴圈語法
在 C# 裡,巢狀迴圈的語法跟一般迴圈沒兩樣——你只要在一個迴圈的 主體裡面 再寫一個就好。來看一下 for 跟 while 的例子:
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Console.Write($"{i},{j} ");
}
Console.WriteLine();
}
這裡外層迴圈控制變數 i(從 0 到 2),內層控制 j(從 0 到 3)。每個 i,內層都會從 j=0 跑到 j=3。執行這段程式,你會看到很整齊的座標表格:
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
用 while 也可以做到一樣:
int i = 0;
while (i < 3)
{
int j = 0;
while (j < 4)
{
Console.Write($"{i},{j} ");
j++;
}
Console.WriteLine();
i++;
}
注意:每次外層跑的時候,內層的變數(j)都要重新初始化,不然你只會看到一行!
3. 巢狀迴圈的實戰例子
例子 1:印出棋盤(8x8)
我們第一個任務——用巢狀 for 迴圈印出經典的棋盤(黑白格,# 代表黑格,_ 代表白格):
for (int row = 0; row < 8; row++)
{
for (int col = 0; col < 8; col++)
{
if ((row + col) % 2 == 0)
Console.Write("_");
else
Console.Write("#");
}
Console.WriteLine();
}
結果:
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
重點:巢狀的好處就是每一行(row)都能完整走過所有欄(col)。如果沒巢狀,只會有一行或一欄,根本不像棋盤。
例子 2:乘法表
這是巢狀迴圈的經典範例!我們來印出 1-9 的乘法表:
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
Console.Write($"{i * j,3} ");
}
Console.WriteLine();
}
${i * j,3} 這個格式化會自動補空格,讓表格看起來很整齊。
結果:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
...
9 18 27 36 45 54 63 72 81
4. 巢狀迴圈的控制細節
巢狀迴圈裡 break 跟 continue 的影響
這裡很多新手會搞混!如果你在內層用 break 或 continue,它只會影響 那一層。外層還是會繼續跑,完全沒事。
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == 3)
break; // 只跳出內層!
Console.Write($"{i},{j} ");
}
Console.WriteLine();
}
結果:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
如果你想一次跳出兩層(比如在表格裡一找到就要馬上結束),通常會用 flag 或特殊寫法(像 return,如果在 function 裡)。
5. 巢狀迴圈的視覺化
有時候很難「看到」巢狀迴圈的執行順序。來看這個流程圖:
用表格來看——如果 i 從 1 到 3,j 從 1 到 4,總共會有幾次迭代?
| i | j(每個 i 都會跑這些) | 內層迴圈次數 |
|---|---|---|
| 1 | 1, 2, 3, 4 | 4 |
| 2 | 1, 2, 3, 4 | 4 |
| 3 | 1, 2, 3, 4 | 4 |
| 總共:3 × 4 = 12 |
6. 巢狀迴圈常見錯誤與陷阱
常見錯誤——內層變數初始化錯誤。比如你在外層宣告,卻沒每次都重設,結果內層可能根本不會跑,或是跑錯。
int j = 0;
for (int i = 0; i < 3; i++)
{
while (j < 4) // 哎呀!j 在第一次後可能已經等於 4 了。
{
Console.Write($"{i},{j} ");
j++;
}
Console.WriteLine();
}
這樣只會跑一次。記得內層變數一定要 在外層裡面 初始化!
還有,如果你不小心在巢狀裡用一樣的變數名,編譯器會罵你:變數已經宣告過了。
for (int i = 0; i < 3; i++)
{
for (int i = 0; i < 3; i++)
{
Console.Write($"{i},{i} ");
}
Console.WriteLine();
}
GO TO FULL VERSION