杨辉三角相信大家都不陌生,这是一道经典的编程题目,它的解法多种多样,在组合数学、概率统计和多项式展开中也有重要应用。
今天,我就带大家从最常见的开始,逐渐对它的解法进行优化迭代。
题目描述
给定一个非负整数 numRows
,生成杨辉三角的前 numRows
行。在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
ini
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
解法1:基础迭代法(双重循环)
核心思想:逐行构建杨辉三角,每行的第一个和最后一个元素为1,中间元素为上一行相邻两个元素之和,可以将原本的等边三角形视作直角三角形解答。
javascript
function generate(numRows) {
if (numRows === 0) return [];
const triangle = [[1]];
for (let i = 1; i < numRows; i++) {
const prevRow = triangle[i - 1];
const currentRow = [1]; // 每行第一个元素总是1
// 计算中间元素
for (let j = 1; j < i; j++) {
currentRow[j] = prevRow[j - 1] + prevRow[j];
}
currentRow.push(1); // 每行最后一个元素总是1
triangle.push(currentRow);
}
return triangle;
}
解题思路:
- 边界处理 :当
numRows
为0时直接返回空数组 - 初始化 :创建第一行
[1]
- 逐行构建 :
- 每行的第一个元素总是1
- 中间元素 = 上一行左上方元素 + 上一行右上方元素
- 每行的最后一个元素总是1
- 添加到结果:将每行加入结果数组
时间复杂度 :O(n²)
空间复杂度:O(n²)(存储整个三角形)
注意点:
- 索引越界 :访问
prevRow[j]
时,要确保j < prevRow.length
- 边界处理 :忘记处理
numRows = 0
的情况 - 行首行尾:每行的第一个和最后一个元素必须设为1
- 初始行:第一行需要单独初始化
解法2:函数式编程(数组映射)
核心思想 :利用数组的 map
和 reduce
方法,以更声明式的方式构建杨辉三角。
javascript
function generate(numRows) {
const triangle = [];
for (let i = 0; i < numRows; i++) {
// 创建当前行数组
const row = Array(i + 1).fill(1);
// 计算中间元素(跳过首尾)
for (let j = 1; j < i; j++) {
row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
}
triangle.push(row);
}
return triangle;
}
优化点:
- 统一初始化 :使用
Array(i + 1).fill(1)
一次性创建并填充数组 - 代码简洁:减少了对首尾元素的特殊处理
- 可读性:更清晰地表达"创建长度为i+1且全为1的数组"的意图
易错点:
- 填充值覆盖 :中间元素计算时会覆盖初始填充的1,确保循环范围正确(
j < i
) - 空数组处理 :当
numRows=0
时返回空数组 - 行索引 :访问
triangle[i-1]
时确保i > 0
解法3:动态规划优化(单数组迭代)
核心思想:只保留上一行数据,减少空间复杂度,同时使用对称性优化计算。
javascript
function generate(numRows) {
if (numRows === 0) return [];
if (numRows === 1) return [[1]];
const triangle = [[1], [1, 1]];
for (let i = 2; i < numRows; i++) {
const prevRow = triangle[i - 1];
const row = [1];
const mid = Math.ceil(i / 2);
// 计算左半部分
for (let j = 1; j < mid; j++) {
row[j] = prevRow[j - 1] + prevRow[j];
}
// 利用对称性复制右半部分
for (let j = mid; j <= i; j++) {
row[j] = row[i - j];
}
triangle.push(row);
}
return triangle;
}
优化点:
- 空间优化:只需访问上一行而非所有历史行
- 对称性利用:杨辉三角左右对称,只需计算一半元素
- 边界处理:单独处理前两行,简化主循环逻辑
易错点:
- 对称索引 :
row[j] = row[i - j]
确保索引计算正确 - 中间点 :
Math.ceil(i/2)
处理奇偶行差异 - 特殊行处理:确保前两行正确初始化
解法4:数学公式法(组合数)
核心思想:利用杨辉三角与组合数的关系(第n行第k个元素 = C(n,k))
javascript
function generate(numRows) {
const factorial = n => {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
const triangle = [];
for (let n = 0; n < numRows; n++) {
const row = [];
for (let k = 0; k <= n; k++) {
// C(n,k) = n! / (k! * (n-k)!)
row.push(factorial(n) / (factorial(k) * factorial(n - k)));
}
triangle.push(row);
}
return triangle;
}
特点:
- 数学本质:直接体现杨辉三角的数学原理
- 精确计算:避免累加误差
- 概念清晰:明确展示杨辉三角与二项式系数的关系
缺点:
- 计算效率:阶乘计算时间复杂度高(O(n³))
- 数值限制:大数阶乘可能导致数值溢出
- 递归深度:阶乘递归可能造成栈溢出
总结与选择建议
- 推荐解法:解法1(基础迭代法),简单直观,适用于大多数场景
- 性能敏感:解法3(动态规划优化),空间效率更高
- 数学探索:解法4(组合数法),适合理解数学本质但不适合实际应用
- 代码简洁:解法2(函数式编程),平衡可读性和性能
杨辉三角不仅是经典的编程题目,更在组合数学、概率统计和多项式展开中有重要应用。理解其生成算法有助于培养动态规划思维和发现数学模式的能力。