力扣刷题攻略数组篇-二维数组及滚动数组(一)


文章内容是自己刷leetcode官方刷题攻略的一些经验与总结。
题目链接详见 leetcode刷题攻略
如果喜欢的话,别忘了点个赞哦 ^ _ ^

一.118杨辉三角

1.题目描述

加粗样式

2.题目分析与解答

为了方便理解,我们可以将杨辉三角转换成计算机存储的方式。如下:

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1

我们可以观察到每一行的头和尾都是1,其他的每个元素都是由上一行该列的元素与上一行前一列的元素求和得到。

不难想到,我们可以创建一个二维数组进行遍历,遇到每一行的头和尾特殊处理一下,其余元素按照上一行同列元素 + 上一行前一列元素得到。

C++代码如下:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> ans(numRows);
        for(int i = 0; i < numRows; i ++) {
            ans[i].resize(i + 1); 
            for(int j = 0; j <= i; j ++) {
                if(j == 0 || j == i) {
                    ans[i][j] = 1;
                } else {
                    ans[i][j] = ans[i - 1][j - 1] + ans[i - 1][j];
                }
            }
        }

        return ans;
    }
};

二.119杨辉三角II

1.问题描述

在这里插入图片描述

2.题目分析与解答

下面分为四个小模块循序渐进从时间复杂度和空间复杂度进行优化:

  • 暴力解法
  • 滚动数组
  • 单数组滚动
  • 线性递推
1.暴力解法

有了上一题的基础,我们首先想到的肯定是先把这个杨辉三角创建出来,然后直接返回最后一行也就是答案需要的那一行。

时间复杂度O(n^2),空间复杂度较大,需要一个二维数组。

C++代码如下:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        //1.可以创建出具有rowIndex行的杨辉三角,然后再返回第rowIndex行的数据

        vector<vector<int>> ans(rowIndex + 1);
        for(int i = 0; i <= rowIndex; i ++) {
            ans[i].resize(i + 1);
            for(int j = 0; j <= i; j ++) {
                if(j == 0 || j == i) ans[i][j] = 1;
                else {
                    ans[i][j] = ans[i - 1][j - 1] + ans[i - 1][j];
                }
            }
        }

        return ans[rowIndex];
    }
};

那么以上代码可以优化吗?

2.滚动数组

其实根据上一题的分析,我们发现要求第 n 行的杨辉三角,其实只需要第 n-1 行的数据,那么根据这个特性,可以使用滚动数组。也就是只用两个数组交替存储数据即可

时间复杂度O(n^2),但是空间复杂度更小了,只有两个数组。

C++代码如下:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        //1.可以创建出具有rowIndex行的杨辉三角,然后再返回第rowIndex行的数据
        //2.优化:第i+1行只用到了第i行的数据,可以用滚动数组

        vector<int> pre, cur;
        for(int i = 0; i <= rowIndex; i ++) {
            cur.resize(i + 1);
            for(int j = 0; j <= i; j ++) {
                if(j == 0 || j == i) cur[j] = 1;
                else cur[j] = pre[j - 1] + pre[j];
            }
            pre = cur;
        }

        return pre;
    }
};

其实还可以进一步优化。

3.单数组滚动

由数学推导可知,当前行的第 i 个元素只与上一行的第 i 个元素与上一行的第 i - 1 个元素有关。那么我们可以倒着遍历,这样计算第 i 项的元素时,第 i - 1 项仍然是上一行的值。所以使用一个数组就可以实现。

时间复杂度O(n),空间上只使用了一个额外数组

C++代码如下:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        //1.可以创建出具有rowIndex行的杨辉三角,然后再返回第rowIndex行的数据
        //2.优化:第i+1行只用到了第i行的数据,可以用滚动数组
        //3.进一步优化:只用一个数组。根据数学推导可知,当前第i个元素只与上一行的第i个元素和
        //  第i-1个元素有关,我们可以倒着遍历,这样计算第i项时第i-1项仍是上一行的值

        vector<int> ans(rowIndex + 1);
        for(int i = 0; i <= rowIndex; i ++) {
            for(int j = i; j >= 0; j --) {
                if(j == i || j == 0) ans[j] = 1;
                else ans[j] += ans[j - 1];
            }
        }

        return ans;
    }
};

其实还有更变态的优化,时间复杂度直接降为O(n)。

4.线性递推优化

根据数学公式可以知道,杨辉三角其实是二项展开式的系数
公式如下C(m, n) = n! / ( m! * (n-m)! ),其中 n 代表第 n 行,m 从 0 遍历到 n。就可以得到每一行各个位置的元素。

我们可以发现:
C(m-1, n) = n! / ( (m-1)! * (n-m+1)! )
那么 C(m, n) = C(m-1, n) * (n-m+1) / m
递推的开始为 C(0, n) = 1。

时间复杂度为O(n),空间上用了一个额外数组。

C++代码如下:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        //方法1,2,3时间复杂度O(n^2),方法4时间复杂度O(n)
        //1.可以创建出具有rowIndex行的杨辉三角,然后再返回第rowIndex行的数据
        //2.优化:第i+1行只用到了第i行的数据,可以用滚动数组
        //3.进一步优化:只用一个数组。根据数学推导可知,当前第i个元素只与上一行的第i个元素和
        //  第i-1个元素有关,我们可以倒着遍历,这样计算第i项时第i-1项仍是上一行的值
        //4.最终优化,线性递推:由数学公式推导C(m,n) = n! / (m!) * ((n-m)!)
        //  C(m-1,n) = n! / ((m-1)!) * ((n-m+1)!)
        //  C(m,n) = C(m-1,n) * (n-m+1) / m
        //  C(0,n) = 1

        vector<int> ans(rowIndex + 1);
        ans[0] = 1;
        for(int i = 1; i <= rowIndex; i ++) {
            ans[i] = (long long) ans[i - 1] * (rowIndex - i + 1) / i; //这里乘法可能会溢出
        }
        return ans;
    }
};
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Echo夏末

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值