「LeetCode」119.杨辉三角 II

208 阅读2分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

题目描述🌍

给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

img

示例 1

输入: rowIndex = 3
输出: [1,3,3,1]

示例 2

输入: rowIndex = 0
输出: [1]

示例 3

输入: rowIndex = 1
输出: [1,1]

提示

  • 0 <= rowIndex <= 33

进阶:你可以优化你的算法到 O(rowIndex)O(rowIndex) 空间复杂度吗?

暴力求解💦

解题思路

参考上一篇求整个「杨辉三角」的思路,我们可以先通过 Cni=Cn1i+Cn1i1C^i_n=C^i_{n-1}+C^{i-1}_{n-1} 公式逐行求解杨辉三角前 rowIndex + 1 行,然后返回非负索引 rowIndex 对应的那一行即可。

代码

Java

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<List<Integer>> triangle = new ArrayList<>();
        for (int i = 0; i <= rowIndex; i++) {
            List<Integer> row = new ArrayList<>();
            for (int j = 0; j <= i; j++) {
                if (j == 0 || j == i) {
                    row.add(1);
                } else {
                    row.add(triangle.get(i - 1).get(j - 1) + triangle.get(i - 1).get(j));
                }
            }
            triangle.add(row);
        }
        return triangle.get(rowIndex);
    }
}

C++

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<vector<int>> ret(rowIndex + 1);
        for (int i = 0; i < rowIndex + 1; ++i) {
            ret[i].resize(i + 1);
            ret[i][0] = ret[i][i] = 1;
            for (int j = 1; j < i; ++j) {
                ret[i][j] = ret[i - 1][j - 1] + ret[i - 1][j];
            }
        }
        return ret[rowIndex];
    }
};

时间复杂度:O(rowIndex2)O(rowIndex^2)

空间复杂度:O(rowIndex2)O(rowIndex^2)

滚动数组「优化暴力解法」🌈

解题思路

滚动数组的作用在于优化空间

是否觉得方法一过于多余?计算第 rowIndex 行竟然要保存前 rowIndex - 1 行的数据,无形中提高了空间复杂度!这里我们通过「滚动数组」的思想优化下空间。

Cni=Cn1i+Cn1i1C^i_n=C^i_{n-1}+C^{i-1}_{n-1} 公式可知,计算第 n 行只需第 n - 1 行的数据,所以只需不断更新上一行数组即可。

代码

Java

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> preLine = new ArrayList<>();
        for (int i = 0; i <= rowIndex; i++) {
            List<Integer> curLine = new ArrayList<>();
            for (int j = 0; j <= i; j++) {
                if (j == 0 || j == i) {
                    curLine.add(1);
                } else {
                    curLine.add(preLine.get(j - 1) + preLine.get(j));
                }
            }
            preLine = curLine;
        }
        return preLine;
    }
}

C++

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> cur_line, pre_line;
        for (int i = 0; i < rowIndex + 1; ++i) {
            cur_line.resize(i + 1);
            cur_line[0] = cur_line[i] = 1;
            for (int j = 1; j < i; ++j) {
                cur_line[j] = pre_line[j - 1] + pre_line[j];
            }
            pre_line = cur_line;
        }
        return cur_line;
    }
};

时间复杂度:O(rowIndex2)O(rowIndex^2)

空间复杂度:O(rowIndex)O(rowIndex)

补位逆向递推「优化滚动数组」👑

解题思路

由方法二 preLine = curLine 代码可以看出在计算过程中,当前行在计算过程中保存着上一行的数据,所以我们通过当前行自身来获取 Cn1iC^i_{n-1}Cn1i1C^{i-1}_{n-1} 元素,然后对当前行元素逆序修改(从后往前覆盖才不会丢失上一行所留下的数据)。

:因为每一行都比上一行多一个元素,所以递推过程中要补一个末位元素。

代码

Java

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> row = new ArrayList<>();
        row.add(1);
        for (int i = 0; i < rowIndex; i++) {
            // 首先添加末位元素
            row.add(1);
            // 逆序修改元素
            for (int j = i; j > 0; j--) {
                row.set(j, row.get(j) + row.get(j - 1));
            }
        }
        return row;
    }
}

C++

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> row(rowIndex + 1);
        row[0] = 1;
        for (int i = 0; i < rowIndex; ++i) {
            row[i + 1] = 1;
            for (int j = i; j > 0; --j) {
                row[j] = row[j] + row[j - 1];
            }
        }
        return row;
    }
};

时间复杂度:O(rowIndex2)O(rowIndex^2)

空间复杂度:O(1)O(1)

前后递推求解🚀

前三种方法都用到了「动态规划」的思想,但其实杨辉三角的某一行可以直接求解。

解题思路

由公式 Cnm=n!m!(nm)!C^m_n=\frac{n!}{m!(n-m)!}Cnm1=n!(m1)!(nm+1)!C^{m-1}_n=\frac{n!}{(m-1)!(n-m+1)!} 可以推出前后元素间的递推公式: Cnm=Cnm1×nm+1mC^m_n=C^{m-1}_n\times\frac{n-m+1}{m}

⭐注:求解元素过程中可能会溢出 int 数据类型的范围 (231,2321)(-2^{31}, 2^{32}-1): 所以计算过程中先转为 long 类型再将最终值转为 int 类型。

代码

Java

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> row = new ArrayList<>();
        row.add(1);
        for (int i = 1; i <= rowIndex; i++) {
            row.add((int) ((long) row.get(i - 1) * (rowIndex - i + 1) / i));
        }
        return row;
    }
}

C++

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<long> row_long(rowIndex + 1);
        row_long[0] = 1;
        for (int i = 1; i < rowIndex + 1; ++i) {
            row_long[i] = row_long[i - 1] * (rowIndex - i + 1) / i;
        }
        // casting vector<long> to vector<int>, only method..
        vector<int> row(row_long.begin(), row_long.end());
        return row;
    }
};

时间复杂度: O(rowIndex)O(rowIndex)

空间复杂度: O(1)O(1)

最后🌅

该篇文章为 「LeetCode」 系列的 No.13 篇,在这个系列文章中:

  • 尽量给出多种解题思路
  • 提供题解的多语言代码实现
  • 记录该题涉及的知识点

👨‍💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!