每日LeetCode : 杨辉三角

杨辉三角相信大家都不陌生,这是一道经典的编程题目,它的解法多种多样,在组合数学、概率统计和多项式展开中也有重要应用。

今天,我就带大家从最常见的开始,逐渐对它的解法进行优化迭代。

题目描述

给定一个非负整数 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;
}

解题思路

  1. 边界处理 :当 numRows 为0时直接返回空数组
  2. 初始化 :创建第一行 [1]
  3. 逐行构建
    • 每行的第一个元素总是1
    • 中间元素 = 上一行左上方元素 + 上一行右上方元素
    • 每行的最后一个元素总是1
  4. 添加到结果:将每行加入结果数组

时间复杂度 :O(n²)
空间复杂度:O(n²)(存储整个三角形)

注意点

  1. 索引越界 :访问 prevRow[j] 时,要确保 j < prevRow.length
  2. 边界处理 :忘记处理 numRows = 0 的情况
  3. 行首行尾:每行的第一个和最后一个元素必须设为1
  4. 初始行:第一行需要单独初始化

解法2:函数式编程(数组映射)

核心思想 :利用数组的 mapreduce 方法,以更声明式的方式构建杨辉三角。

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;
}

优化点

  1. 统一初始化 :使用 Array(i + 1).fill(1) 一次性创建并填充数组
  2. 代码简洁:减少了对首尾元素的特殊处理
  3. 可读性:更清晰地表达"创建长度为i+1且全为1的数组"的意图

易错点

  1. 填充值覆盖 :中间元素计算时会覆盖初始填充的1,确保循环范围正确(j < i
  2. 空数组处理 :当 numRows=0 时返回空数组
  3. 行索引 :访问 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;
}

优化点

  1. 空间优化:只需访问上一行而非所有历史行
  2. 对称性利用:杨辉三角左右对称,只需计算一半元素
  3. 边界处理:单独处理前两行,简化主循环逻辑

易错点

  1. 对称索引row[j] = row[i - j] 确保索引计算正确
  2. 中间点Math.ceil(i/2) 处理奇偶行差异
  3. 特殊行处理:确保前两行正确初始化

解法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;
}

特点

  1. 数学本质:直接体现杨辉三角的数学原理
  2. 精确计算:避免累加误差
  3. 概念清晰:明确展示杨辉三角与二项式系数的关系

缺点

  1. 计算效率:阶乘计算时间复杂度高(O(n³))
  2. 数值限制:大数阶乘可能导致数值溢出
  3. 递归深度:阶乘递归可能造成栈溢出

总结与选择建议

  • 推荐解法:解法1(基础迭代法),简单直观,适用于大多数场景
  • 性能敏感:解法3(动态规划优化),空间效率更高
  • 数学探索:解法4(组合数法),适合理解数学本质但不适合实际应用
  • 代码简洁:解法2(函数式编程),平衡可读性和性能

杨辉三角不仅是经典的编程题目,更在组合数学、概率统计和多项式展开中有重要应用。理解其生成算法有助于培养动态规划思维和发现数学模式的能力。

相关推荐
Felven8 分钟前
B. Lasers
算法
饕餮怪程序猿14 分钟前
订单分批算法设计与实现:基于商品相似性的智能分拣优化(C++)
开发语言·c++·算法
开心猴爷18 分钟前
APP 上架苹果 App Store 被拒,并不总是产品问题
后端
Li_76953228 分钟前
Redis —— (五)
java·redis·后端·spring
用户479492835691536 分钟前
你每天都在用的 JSON.stringify ,V8 给它开了“加速通道”
前端·chrome·后端
狗狗摇屁屁38 分钟前
JS手写防抖
开发语言·javascript·ecmascript
剪一朵云爱着39 分钟前
PAT 1091 Acute Stroke
算法·pat考试
子夜江寒41 分钟前
基于 Python 库使用贝叶斯算法与逻辑森林
开发语言·python·算法
JIngJaneIL42 分钟前
基于java+ vue办公管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
uzong1 小时前
如何将项目做出 owner 的感觉
后端