C 练习实例61 – 杨辉三角形(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言:探索 C 语言中的杨辉三角形之美

在编程学习的旅程中,算法实践是提升逻辑思维与代码能力的重要途径。杨辉三角形作为经典算法案例,不仅体现了数学与编程的完美结合,更是 C 语言练习中不可或缺的进阶课题。本文将以“C 练习实例61 – 杨辉三角形”为核心,通过深入浅出的讲解,帮助初学者掌握其数学原理与实现方法,并为中级开发者提供优化思路与扩展方向。


数学原理与生成规则:金字塔般的数字世界

杨辉三角形是数学中一种特殊排列的数表,其核心规则可概括为:

  1. 每行的两端均为 1
  2. 中间的每个数字等于上一行相邻两个数字之和。

这一规则的数学表达式为:
$$
\text{第 }n\text{ 行第 }k\text{ 个元素} = \binom{n-1}{k-1}
$$
其中 $\binom{n}{k}$ 为组合数,即从 $n$ 个不同元素中选取 $k$ 个的组合方式数量。例如,第 4 行的元素为 1 3 3 1,对应组合数 $\binom{3}{0}, \binom{3}{1}, \binom{3}{2}, \binom{3}{3}$。

形象化比喻:建筑中的数字积木

可以将杨辉三角形想象为一座金字塔:

  • 塔尖(第一行)仅有一个积木块(数字 1);
  • 每一层的积木块数量比上一层多一个;
  • 中间的积木是通过叠加“上方左右两侧的积木”构建而成。

这种层级递进的生成方式,完美体现了递推算法的核心思想。


C 语言实现杨辉三角形的代码示例

基础版:二维数组存储法

通过二维数组存储每一行的数值,是实现杨辉三角形的基础方法。以下是具体实现步骤:

#include <stdio.h>

#define MAX_ROWS 30  // 最大行数限制

int main() {
    int triangle[MAX_ROWS][MAX_ROWS] = {0};
    int rows, i, j;

    printf("请输入要生成的杨辉三角形行数(≤%d):", MAX_ROWS);
    scanf("%d", &rows);

    // 初始化第一行
    triangle[0][0] = 1;

    for (i = 1; i < rows; i++) {
        triangle[i][0] = 1;      // 每行第一个元素为 1
        for (j = 1; j < i; j++) {
            // 当前行的中间元素 = 上一行的前一个元素 + 上一行的当前元素
            triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
        }
        triangle[i][i] = 1;      // 每行最后一个元素为 1
    }

    // 输出结果
    for (i = 0; i < rows; i++) {
        // 打印每行前导空格(使图形居中对齐)
        for (j = 0; j < rows - i - 1; j++) {
            printf("   ");
        }
        for (j = 0; j <= i; j++) {
            printf("%6d", triangle[i][j]);
        }
        printf("\n");
    }

    return 0;
}

代码解析:

  1. 二维数组初始化:使用 triangle[MAX_ROWS][MAX_ROWS] 存储所有行的数据;
  2. 逐行填充
    • 第一行直接赋值 triangle[0][0] = 1
    • 后续每一行通过双重循环计算中间元素,公式为:
      triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]
  3. 格式化输出:通过前导空格调整对齐方式,使三角形居中显示。

优化版:逐行计算法(节省内存)

若行数较大时,二维数组可能占用过多内存。此时可通过逐行计算的方式,仅保留上一行数据:

#include <stdio.h>

#define MAX_ROWS 30

int main() {
    int current_row[MAX_ROWS] = {0};
    int previous_row[MAX_ROWS] = {0};
    int rows, i, j;

    printf("请输入行数(≤%d):", MAX_ROWS);
    scanf("%d", &rows);

    // 处理第一行
    current_row[0] = 1;

    for (i = 0; i < rows; i++) {
        // 输出当前行前导空格
        for (j = 0; j < rows - i - 1; j++) {
            printf("   ");
        }

        // 计算当前行元素
        for (j = 0; j <= i; j++) {
            if (j == 0) {
                current_row[j] = 1;
            } else {
                current_row[j] = previous_row[j-1] + previous_row[j];
            }
            printf("%6d", current_row[j]);
        }

        // 将当前行复制为下一行的上一行
        for (j = 0; j <= i; j++) {
            previous_row[j] = current_row[j];
        }

        printf("\n");
    }

    return 0;
}

优化点说明:

  • 空间复杂度降低:仅使用两个一维数组 current_rowprevious_row,空间复杂度为 $O(n)$;
  • 数据传递:通过循环将当前行的值赋给上一行数组,避免保留所有历史数据。

常见问题与调试技巧

问题1:输出结果错位

现象:数值对齐不美观,或出现多余数字。
原因:输出格式字符串中的占位符设置不当。
解决方案

  • 使用 printf("%6d", num) 为每个数字预留 6 个字符宽度;
  • 前导空格通过 rows - i - 1 控制,确保三角形对称。

问题2:数值溢出

现象:当行数较大时(如超过 30 行),数值可能超出 int 类型范围。
解决方案

  • 改用 long long 类型存储数值;
  • 或通过数学公式 $\binom{n}{k} = \frac{n!}{k!(n-k)!}$ 直接计算(需实现阶乘函数,但效率较低)。

进阶扩展:动态行数与图形化输出

动态内存分配

若需处理非常大的行数,可结合动态内存分配:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int rows;
    int **triangle;

    printf("请输入行数:");
    scanf("%d", &rows);

    // 动态分配二维数组
    triangle = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        triangle[i] = (int *)malloc((i + 1) * sizeof(int));
    }

    // 初始化第一行
    triangle[0][0] = 1;

    for (int i = 1; i < rows; i++) {
        triangle[i][0] = 1;
        for (int j = 1; j < i; j++) {
            triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
        }
        triangle[i][i] = 1;
    }

    // 输出并释放内存
    // ...(与之前代码类似)
    for (int i = 0; i < rows; i++) {
        free(triangle[i]);
    }
    free(triangle);

    return 0;
}

图形化输出示例

通过 ASCII 字符模拟更复杂的几何图案:

// 在输出数值后添加特殊符号
printf("%6d%c", triangle[i][j], (j % 2 == 0) ? '*' : '#');

结论:从基础到进阶的思维跃迁

通过本篇文章的讲解,我们不仅实现了杨辉三角形的 C 语言程序,更深入理解了:

  1. 递推算法的核心思想;
  2. 二维数组与动态内存的使用场景;
  3. 数学与编程的结合方法。

对于初学者,建议从基础版代码入手,逐步调试并理解每一行的逻辑;中级开发者则可尝试优化内存使用或扩展功能。杨辉三角形虽看似简单,但它展现了算法设计中“以小见大”的智慧——通过局部规则的递推,构建出全局的复杂结构。

在后续学习中,可将此案例拓展至其他领域,例如:

  • 使用递归函数生成杨辉三角形;
  • 结合图形库绘制三维立体效果;
  • 探索其在组合数学、概率论中的实际应用。

愿本文成为您编程之路上的一块基石,助您在算法探索中不断突破自我!

最新发布