今天我们一起掌握 leetocode 的 43, 46, 53 题。涉及到3种题型,值得一做。
1. lc 43 - 字符串相乘
- 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
- 难度:中等
注意:不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
1.1 解题思路 - 模拟「竖式乘法」
由题目知,不能直接将输入转换为整数来处理,例如 int(num1)。因此,我们可以采用我们平时在纸上计算乘数的思路解题。
此题中,以 num1 为被乘数, num2 为乘数。从右往左遍历乘数,将乘数的每一位与被乘数相乘得到对应的结果,再将每次得到的结果累加。
同时,注意除了最低位以外,其余的每一位的运算结果都需要补0。
具体流程图:
1.2 时间复杂度
- 时间复杂度:O(m * n+n^2),其中 m,n 是 num1, num2 的长度。需要从右往左遍历 num2,对于 num2 的每一位,都需要和 num1 的每一位计算乘积,因此计算乘积的总次数是 mn。字符串相加操作共有 n 次,相加的字符串长度最长为 m+n,因此字符串相加的时间复杂度是 O(m * n+n^2)
- 空间复杂度:O(m+n)。空间复杂度取决于存储中间状态的字符串,由于乘积的最大长度为 m+n,因此存储中间状态的字符串的长度不会超过 m+n。
1.3 代码
1 | class Solution: |
2. lc 46 - 全排列
- 给定一个 没有重复 数字的序列,返回其所有可能的全排列。
- 难度:中等
2.1 解题思路 - 递归
看了部分解析,皆采用回溯法。今天,我们将用简单的递归,一样也能解题。
我们会用到recursion(choosen:list,candidates:list)递归函数。choosen会储存被选中加入排序的元素,剩下的没被选择的,作为排列的候选。
举例,nums = [1,2,3,4],则第一个递归choosen为[1],candidates = [2,3,4];第二次递归中,choosen为[2],candidates = [1,3,4],以此类推。
2.2 时间复杂度
- 时间复杂度:O(n*n!)。递归算法的时间复杂度本质上是要看: 递归的次数 * 每次递归中的操作次数。显然代码中递归次数是排列的可能情况 n!,而每次递归操作最坏情况为 n(遍历长度为n的candidates)。
- 空间复杂度:O(n)。其中 n 为序列的长度。除答案数组以外,递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,这里可知递归调用深度为 O(n) 。
2.3 代码
1 | class Solution: |
3. lc 53 - 连续子数组的最大和
- 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 要求时间复杂度为O(n)。
- 难度:简单
3.1 解题思路 - 动态规划
我们用遍历输入数组 nums 时,所有以当前元素 nums[i] 为结尾的连续子数组最大和,是可以获得的。这个值 max_value 必然是当前元素和(0或者一个正值)的加和。若这个值也是正值,又可以作为 i+1 时的正值。
用一个额外数组 res 储存 nums 每一位时对应的max_value。然后求取 res 中的最大值,也就是 nums 每一个 nums[i] 为结尾的连续子数组最大和,之中的最值,便是整个 nums 所有子数组的和的最大值。
此外,我们也可以用「动态规划」的思想解题:
3.2 时间复杂度
- 时间复杂度:O(n) 线性遍历数组
- 空间复杂度:O(n)
- 改进:空间复杂度:O(1):由于省去 dp 列表使用的额外空间,因此空间复杂度从 O(n) 降到 O(1)
3.3 代码
1 | class Solution: |
优化空间复杂度:
1 | class Solution: |
参考
[1] 递归的时间复杂度计算. https://www.zhihu.com/question/63075755
[2] lc 53 题解. https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/solution/mian-shi-ti-42-lian-xu-zi-shu-zu-de-zui-da-he-do-2/