原始錯誤代碼問題
#define m 10
#define n 10
int yang[m][n];
for (i = 0; i <= m; i++) { // ❌ 越界:m=10,下標 0..9,i<=10 訪問 yang[10]
for (j = 0; j <= n; j++) { // ❌ 同理越界
if (i == j || j == 0)
yang[i][j] = 1;
else
yang[i][j] = yang[i-1][j-1] + yang[i-1][j]; // i=0 時 yang[-1] 崩潰
printf("%5d ", yang[i][j]);
}
if (j == i) printf("\n"); // ❌ j 已經是 n+1,永遠不等於 i
}
修正版:動態分配 + 邊界安全 + 格式化
#include
#include
void print_pascal(int rows) {
// 動態分配二維數組(行長度遞增,節省空間)
int **tri = malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
tri[i] = calloc(i + 1, sizeof(int)); // 第 i 行有 i+1 個元素
tri[i][0] = tri[i][i] = 1; // 兩邊為 1
for (int j = 1; j < i; j++) // 內部元素
tri[i][j] = tri[i-1][j-1] + tri[i-1][j];
}
// 格式化輸出:等腰三角居中
int max_width = 4 * rows; // 估算最後一行寬度
for (int i = 0; i < rows; i++) {
// 前導空格
for (int s = 0; s < (rows - i - 1) * 2; s++) putchar(' ');
for (int j = 0; j <= i; j++)
printf("%-4d ", tri[i][j]); // 左對齊固定寬度
puts("");
}
// 釋放內存
for (int i = 0; i < rows; i++) free(tri[i]);
free(tri);
}
int main() {
int n;
printf("請輸入行數 (1-20): ");
scanf("%d", &n);
if (n > 0 && n <= 20) print_pascal(n);
return 0;
}
關鍵修正點對比
| 錯誤點 | 原代碼 | 修正代碼 | 原理 |
| 數組越界 | i <= m | i < rows | C 數組下標 0..size-1 |
| 負下標訪問 | yang[i-1][j-1] (i=0) | 單獨處理第 0 行、第 i 行首尾 | 邊界條件顯式判斷 |
| 靜態定長浪費 | int yang[10][10] | 動態分配 calloc(i+1) | 第 i 行只需 i+1 個元素 |
| 換行邏輯錯 | if (j == i) (j 已越界) | 內層迴圈結束後 puts("") | 迴圈結構清晰 |
| 對齊亂碼 | %5d 固定寬度 | 前導空格 + %-4d 左對齊 | 等腰三角視覺效果 |
遞歸版本(教學展示數學定義)
int pascal(int row, int col) {
if (col == 0 || col == row) return 1;
return pascal(row-1, col-1) + pascal(row-1, col);
}
// 極其低效 O(2^n),僅供理解遞歸定義,實際用迭代/動態規劃
void print_recursive(int rows) {
for (int i = 0; i < rows; i++) {
for (int s = 0; s < (rows-i-1)*2; s++) putchar(' ');
for (int j = 0; j <= i; j++)
printf("%-4d ", pascal(i, j));
puts("");
}
}
總結
- 楊輝三角本質是 **二項式系數 C(n,k) = C(n-1,k-1) + C(n-1,k)** 的動態規劃表。
- **邊界條件**(首行、兩邊)必須顯式初始化,避免負下標/越界。
- **動態分配** 既節省空間又天然匹配三角形狀,配合
calloc 自動歸零。
- **格式化輸出** 需計算前導空格,
%-4d 左對齊固定寬度產生等腰視覺。
- **內存釋放** 必須配對:先釋放行指針,再釋放行指針數組。