题目描述
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
示例 :
输入: 3
输出: [1,3,3,1]
进阶:
- 你可以优化你的算法到 O(k) 空间复杂度吗?
分析与代码
- 杨辉三角的五条性质
- 第 n 行的数字个数为 n。
- 左右数字对称,从 1 开始到 1 结束。
- 第 n 行的第 k 个数字为 (C_{n-1}^{k-1}) 。
- 第 n 行数字之和为 (2^{n-1})。
- 除了每行的第一个数字和最后一个数字,第 n 行的第 k 个数字等于第 n - 1 行的第 k - 1 个数字与第 n - 1 行 的第 k 个数字之和。
- 注意:题目所说的行号是从 0 开始的。
解法一:两个List
- 使用 pre 集合保存前一行的结果,每次循环创建一个新的集合 cur 进行计算,并更新 pre 为 cur。
代码:
class Solution {
public List<Integer> getRow(int rowIndex) {
List<Integer> pre = new ArrayList<Integer>();
pre.add(1);
for (int i = 1; i <= rowIndex; i++) {
List<Integer> cur = new ArrayList<>();
cur.add(1);
for (int j = 1; j < i; j++) {
cur.add(pre.get(j - 1) + pre.get(j));
}
cur.add(1);
pre = cur;
}
return pre;
}
}
解法二、一个List
- 只用一个 List,既当 pre,也当 cur。
- 更新操作改为用
set()
方法,每轮循环加上最后的 1。 - 不能直接写
row.set(j, row.get(j - 1) + row.get(j));
,因为set()
方法会改变当前 j 下标的值,而计算下一个 j 又需要用到之前的值,所以我们用一个变量 pre 来保存,而且每行第一个为 1,每次循环初始 pre 为 1.
代码:
class Solution {
public List<Integer> getRow(int rowIndex) {
List<Integer> row = new ArrayList<Integer>();
row.add(1);
for (int i = 1; i <= rowIndex; i++) {
int pre = 1;
for (int j = 1; j < i; j++) {
int temp = row.get(j);
row.set(j, pre + row.get(j));
pre = temp;
}
row.add(1);
}
return row;
}
}
其实也可以倒着来更新,这样就不会覆盖掉旧值。
class Solution {
public List<Integer> getRow(int rowIndex) {
List<Integer> row = new ArrayList<Integer>();
row.add(1);
for (int i = 1; i <= rowIndex; i++) {
for (int j = i - 1; j >= 1; j--) {
row.set(j, row.get(j - 1) + row.get(j));
}
row.add(1);
}
return row;
}
}
解法三、LinkedList队列
- 每行循环时,先加入 1;从第二个开始,加入队列的第一个数与第二个数之和,然后移出队列第一个数,直至倒数第二个;最后加上最后一个 1,再移出第一个数。
代码:
class Solution {
public List<Integer> getRow(int rowIndex) {
LinkedList<Integer> row = new LinkedList<Integer>();
row.add(1);
for (int i = 1; i <= rowIndex; i++) {
row.add(1);
for (int j = 1; j < i; j++) {
row.add(row.get(0) + row.get(1));
row.poll();
}
row.add(1);
row.poll();
}
return row;
}
}
小结
最容易想到的就是保存上一行的做法,但认真分析又发现可以只用一行也可以,更新时的覆盖旧值问题倒着来更新也不存在了,最后又看到一插一删的队列方法。