杨辉三角 图解题解
这道题到底在问什么
- 输入
- numRows = 6
- 输出
- [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],[1,5,10,10,5,1]]
最优解:为什么这么做
一句话答案:LeetCode 118 杨辉三角:逐行递推,每行两端为 1、中间每格等于上一行相邻两数之和。这条帕斯卡法则就是组合数 C(i,j)=C(i-1,j-1)+C(i-1,j),省去逐格算阶乘的溢出与重复,时间 O(n²)、空间 O(n²)。
杨辉三角这道题到底要我们把什么填出来
给一个行数 numRows,摆出杨辉三角的前 numRows 行。它是个数字三角:第 0 行只有一个 1,往下每行多一个数,两端都是 1,中间每个数等于它「上一行」紧挨的两个数之和。题面给 numRows = 6,要返回前 6 行。后面说第 i 行第 j 个,行号 i、列号 j 都从 0 数起。
为什么照组合公式逐格算阶乘既慢又会溢出
杨辉三角第 i 行第 j 个数,其实就是组合数 C(i,j)(从 i 个东西里挑 j 个的取法数)。照定义每格单独算 C(i,j)=i!/(j!(i−j)!),要反复乘阶乘(i! 就是 1×2×…×i),前面算过的又从头乘一遍。更麻烦的是阶乘涨得极快,行数稍大 i! 就冲破整数范围、乘出的大数会溢出,可那格的值明明不大,逐格硬算并不划算。
相邻两数相加背后是帕斯卡法则的组合直觉
换一个只用加法的算法,靠一条组合恒等式:C(i,j)=C(i-1,j-1)+C(i-1,j)。它说的是,从 i 个东西里挑 j 个,按「第 i 个选不选」分成两堆——不选它,从前 i-1 个里挑满 j 个,有 C(i-1,j) 种;选它,剩下的名额在前 i-1 个里挑 j-1 个,有 C(i-1,j-1) 种。两堆不重不漏加起来,正好是上一行紧挨的两个数之和,中间每格等于上方两数相加就是这么来的。
每行怎样从上一行推出,两端为何恒等于 1
有了这条法则,就能自底向上(从第 0 行起、每行只用刚填好的上一行往下推)逐行生成,不再碰阶乘。参考代码里每到第 i 行,先开一个长度 i+1 的 row、整排填 1;再让中间的 j 从 1 走到 i-1,把 row[j] 改成上一行的 res[i-1][j-1] 加 res[i-1][j],也就是左上那个数加正上那个数。两端不进这轮修改,所以恒为 1。填完把 row 接到 res 末尾,留给下一行当依据。
拿 numRows = 6 亲手把六行填出来
第 0 行只有 1:[1]。第 1 行两端都是 1、没有中间格:[1,1]。第 2 行中间 1+1=2,得 [1,2,1]。第 3 行左边 1+2=3、右边 2+1=3,得 [1,3,3,1]。第 4 行 1+3=4、3+3=6、3+1=4,得 [1,4,6,4,1]。第 5 行 1+4=5、4+6=10、6+4=10、4+1=5,得 [1,5,10,10,5,1]。六行拼起来和题面输出逐格对上。
漏填两端的 1,最左格为什么会去够上一行不存在的邻居
三角形前 numRows 行一共约 numRows²/2 个数,每个数一次加法就填好,时间 O(n²);这些数直接存进要返回的结果,空间也是 O(n²)。两处边界最容易出岔。第 0 行只有一格,中间那圈 j 从 1 到 i-1 不进,全靠「整排先填 1」兜住,漏了这步第一行就空着。两端的 1 同理:要是让 j 从 0 扫到 i,改最左格会取到上一行并不存在的左邻居,不是越界就是算错。
▶ 动画逐步走查(共 34 步)——想跟着动画一帧帧对照就展开
- 3记住这条规则,下面每一格都在套它。
- 4空表准备好了。左边标行号,从第 0 行最左格开始往下填。
- 5第 0 行只有一个数,它既是开头也是结尾,直接填 1。
- 6每一行最左边一格永远是 1(左边没有邻居可加),填 1。
- 7每一行最右边一格永远是 1(右边没有邻居可加),填 1。
- 8每一行最左边一格永远是 1(左边没有邻居可加),填 1。
- 9算第 2 行第 1 个:看它正上方两个数——左上 1、正上 1,把它们加起来。
- 101 + 1 = 2,填进第 2 行第 1 个。
- 11每一行最右边一格永远是 1(右边没有邻居可加),填 1。
- 12每一行最左边一格永远是 1(左边没有邻居可加),填 1。
- 13算第 3 行第 1 个:看它正上方两个数——左上 1、正上 2,把它们加起来。
- 141 + 2 = 3,填进第 3 行第 1 个。
- 15算第 3 行第 2 个:看它正上方两个数——左上 2、正上 1,把它们加起来。
- 162 + 1 = 3,填进第 3 行第 2 个。
- 17每一行最右边一格永远是 1(右边没有邻居可加),填 1。
- 18每一行最左边一格永远是 1(左边没有邻居可加),填 1。
- 19算第 4 行第 1 个:看它正上方两个数——左上 1、正上 3,把它们加起来。
- 201 + 3 = 4,填进第 4 行第 1 个。
- 21算第 4 行第 2 个:看它正上方两个数——左上 3、正上 3,把它们加起来。
- 223 + 3 = 6,填进第 4 行第 2 个。
- 23算第 4 行第 3 个:看它正上方两个数——左上 3、正上 1,把它们加起来。
- 243 + 1 = 4,填进第 4 行第 3 个。
- 25每一行最右边一格永远是 1(右边没有邻居可加),填 1。
- 26每一行最左边一格永远是 1(左边没有邻居可加),填 1。
- 27算第 5 行第 1 个:看它正上方两个数——左上 1、正上 4,把它们加起来。
- 281 + 4 = 5,填进第 5 行第 1 个。
- 29算第 5 行第 2 个:看它正上方两个数——左上 4、正上 6,把它们加起来。
- 304 + 6 = 10,填进第 5 行第 2 个。
- 31算第 5 行第 3 个:看它正上方两个数——左上 6、正上 4,把它们加起来。
- 326 + 4 = 10,填进第 5 行第 3 个。
- 33算第 5 行第 4 个:看它正上方两个数——左上 4、正上 1,把它们加起来。
- 344 + 1 = 5,填进第 5 行第 4 个。
- 35每一行最右边一格永远是 1(右边没有邻居可加),填 1。
- 36整张杨辉三角填完了。每个内部数都是它正上方两数之和,每行两端都是 1。
⚠️ 容易写错的地方
✗ 错:内层 j 从 0 或到 i 都算
✓ 对:j 只在 1..i-1 算和
两端 j=0、j=i 恒为 1,不该去加
✗ 错:访问 res[i-1] 越界
✓ 对:第 0 行特判(没有上一行)
i=0 时不存在上一行
✗ 错:两端忘了置 1
✓ 对:每行先整行填 1 再改中间
漏了端点会变成 0
完整代码(Python / C++ / Java)
Python
def generate(numRows):
res = []
for i in range(numRows):
row = [1] * (i + 1)
for j in range(1, i):
row[j] = res[i-1][j-1] + res[i-1][j]
res.append(row)
return resC++
vector<vector<int>> generate(int numRows){
vector<vector<int>> res(numRows);
for(int i = 0; i < numRows; i++){
res[i].assign(i + 1, 1);
for(int j = 1; j < i; j++)
res[i][j] = res[i-1][j-1] + res[i-1][j];
}
return res;
}Java
List<List<Integer>> generate(int numRows){
List<List<Integer>> res = new ArrayList<>();
for(int i = 0; i < numRows; i++){
List<Integer> row = new ArrayList<>();
for(int j = 0; j <= i; j++){
if(j == 0 || j == i) row.add(1);
else row.add(res.get(i-1).get(j-1) + res.get(i-1).get(j));
}
res.add(row);
}
return res;
}复杂度
时间
O(n²)
三角共约 n²/2 个数,每个 O(1)
空间
O(n²)
存下整张三角(输出本身)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 杨辉三角 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
杨辉三角和组合数 C(i,j) 到底什么关系?能直接用公式算某一格吗?+
第 i 行第 j 个数就等于组合数 C(i,j)。如果只要某一格、不要整张三角,确实能用公式直接算;更稳的写法是同一行内递推 C(i,j)=C(i,j-1)×(i-j+1)/j,从左边一格滚过来,避开大阶乘溢出。但要整张三角时,逐行相加的写法一次把所有格都填了,比每格单独套公式省得多。
能不能只用一维数组、不存整张三角来省空间?+
如果题目只要某一行(LeetCode 119 杨辉三角 II),可以只留一行,从右往左原地更新 row[j] 加上 row[j-1],空间压到 O(n)。从右往左是关键:这样改 row[j] 时用到的 row[j-1] 还是上一行的旧值,没被本轮覆盖。但本题要返回全部行,整张三角本身就是输出,这份 O(n²) 省不掉。
内层循环为什么是 j 从 1 到 i-1,不是从 0 到 i?+
因为第 0 列和第 i 列(每行两端)恒等于 1,已经在「整排填 1」时设好,不必再算。只有中间的格子才需要用上一行相加,范围正好是 1 到 i-1。要是 j 从 0 开始,算最左格时会取到 res[i-1][j-1] 里 j-1 等于 -1 的位置,够到上一行根本不存在的地方,不是越界就是取错值。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 杨辉三角 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。