C 练习实例61 – 杨辉三角形(长文讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言:探索 C 语言中的杨辉三角形之美
在编程学习的旅程中,算法实践是提升逻辑思维与代码能力的重要途径。杨辉三角形作为经典算法案例,不仅体现了数学与编程的完美结合,更是 C 语言练习中不可或缺的进阶课题。本文将以“C 练习实例61 – 杨辉三角形”为核心,通过深入浅出的讲解,帮助初学者掌握其数学原理与实现方法,并为中级开发者提供优化思路与扩展方向。
数学原理与生成规则:金字塔般的数字世界
杨辉三角形是数学中一种特殊排列的数表,其核心规则可概括为:
- 每行的两端均为 1;
- 中间的每个数字等于上一行相邻两个数字之和。
这一规则的数学表达式为:
$$
\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;
}
代码解析:
- 二维数组初始化:使用
triangle[MAX_ROWS][MAX_ROWS]
存储所有行的数据; - 逐行填充:
- 第一行直接赋值
triangle[0][0] = 1
; - 后续每一行通过双重循环计算中间元素,公式为:
triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]
;
- 第一行直接赋值
- 格式化输出:通过前导空格调整对齐方式,使三角形居中显示。
优化版:逐行计算法(节省内存)
若行数较大时,二维数组可能占用过多内存。此时可通过逐行计算的方式,仅保留上一行数据:
#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_row
和previous_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 语言程序,更深入理解了:
- 递推算法的核心思想;
- 二维数组与动态内存的使用场景;
- 数学与编程的结合方法。
对于初学者,建议从基础版代码入手,逐步调试并理解每一行的逻辑;中级开发者则可尝试优化内存使用或扩展功能。杨辉三角形虽看似简单,但它展现了算法设计中“以小见大”的智慧——通过局部规则的递推,构建出全局的复杂结构。
在后续学习中,可将此案例拓展至其他领域,例如:
- 使用递归函数生成杨辉三角形;
- 结合图形库绘制三维立体效果;
- 探索其在组合数学、概率论中的实际应用。
愿本文成为您编程之路上的一块基石,助您在算法探索中不断突破自我!