[LeetCode] 119. 杨辉三角 II

传送门:[LeetCode] 119. 杨辉三角 II

题目描述

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

小结

最容易想到的就是保存上一行的做法,但认真分析又发现可以只用一行也可以,更新时的覆盖旧值问题倒着来更新也不存在了,最后又看到一插一删的队列方法。


posted @ 2019-09-30 16:48  Qiu_Jiaqi  阅读(147)  评论(0编辑  收藏  举报