这篇博客记录刷题第6天的思路与心得。
43. 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = “2”, num2 = “3” 输出: “6”
示例 2:
输入: num1 = “123”, num2 = “456” 输出: “56088”
说明:
num1 和 num2 的长度小于110。
num1 和 num2 只包含数字 0-9。
num1 和 num2 均不以零开头,除非是数字 0 本身。
不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
分析:这道题我首先想到的是利用栈或者队列,但实际上只要透彻理解手算两数相乘的过程,通过构造int数组存放不同位数的乘积结果就行。最后去除前端多余的0,将int数组元素导入string中。参考评论区,该过程描述如下:
num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
例: 123 * 45, 123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
index: 0 1 2 3 4
1 2 3
* 4 5
---------
1 5
1 0
0 5
---------
0 6 1 5
1 2
0 8
0 4
---------
0 5 5 3 5
这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中
class Solution {
public:
string multiply(string num1, string num2) {
int n1 = num1.length() - 1;
int n2 = num2.length() - 1;
if (n1 < 0 || n2 < 0) return "";
int mul_len = n1+n2+2;
int mul[mul_len];
for (int i = 0; i < mul_len; i++) mul[i] = 0; //初始化为0
//num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
for(int i = n1; i >= 0; i--) {
for (int j = n2; j >= 0; j--) {
int bitmul = (num1.at(i)-'0') * (num2.at(j)-'0');
bitmul += mul[i+j+1]; //先加低位
mul[i+j] += bitmul / 10; //判断低位求和是否进位,不同位数相应存放
mul[i+j+1] = bitmul % 10;
}
}
//去掉前端的0
int i = 0;
while( i < mul_len-1 && mul[i] == 0) {
i++;
}
string ans = "";
for (; i < mul_len; i++) {
string s = to_string(mul[i]);
ans.append(s);
}
return ans;
}
};
值得注意的是,int数组定义后必须初始化为0,否则会各种整幺蛾子,这里记录一下bug:int* mul = new int[n1+n2+2] { 0 }
这行代码会报可变长数组不可初始化的错误,如下图所示。
原来C++11编译器不支持,于是我们只好用for循环初始化,当然也可以通过memset(mul,0,sizeof(mul));
来赋mul数组初值全为0,需要使用头文件string.h。
46.全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1],
[3,1,2], [3,2,1] ]
全排列是利用回溯法的典例,参照官方视频题解,讲得非常透彻。定义一组状态变量,深度优先搜索遍历状态树的所有节点。关于回溯法与深度优先搜索、广义有限搜索、遍历(暴力解法)、动态规化等的区别可以参考如下链接。
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
注意回溯法是以空间换时间的做法。若序列长度为 n n n,则时间、空间复杂度均为 O ( n × n ! ) O(n \times n!) O(n×n!),详细推导如下:
全排列图解如下:
C++代码如下,应该能看懂。
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
if (nums.size() == 0)
return res;
vector<int> path; //状态变量:记录选了哪些数
vector<bool> used(nums.size(), false); //状态变量:记录当前数字是否用过
int depth = 0; //状态变量:记录递归到第几层(用过多少数)
dfs(nums, depth, path, used, res);
return res;
}
private:
void dfs(vector<int>& nums, int depth, vector<int>& path, vector<bool>& used, vector<vector<int>>& res) {
//递归结束条件
if (depth == nums.size()) {
res.push_back(path);
return;
}
//依次使用当前数字
for (int i = 0; i < nums.size(); i++) {
if (!used[i]) {
//当前节点未使用
used[i] = true;
path.push_back(nums[i]);
//递归做深度优先搜索
dfs(nums, depth+1, path, used, res);
//回溯到上一层节点,撤销状态变量
used[i] = false;
path.pop_back();
}
}
return;
}
};
53.最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
这道题用贪心或动态规划可以实现时间复杂度为 O ( n ) O(n) O(n) ,空间复杂度为 O ( 1 ) O(1) O(1);若进一步使用分治,则渐进时间复杂度可提升为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))。参考官方题解如下:
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximum-subarray/solution/zui-da-zi-xu-he-by-leetcode-solution/
(1).贪心法
贪心法求解最大连续子序和的核心思路就是:若当前指针所指元素之前和 < 0, 则丢弃之前的子序列;否则保留之前序列并加入当前元素,存储当前序列和,如此遍历找到最大值。原理如下:
题解和代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, maxAns = nums[0];
for (const auto &x: nums) {
pre = max(pre + x, x); //pre = (pre < 0) ? x : pre+x
maxAns = max(maxAns, pre); //打擂台算法找最大值
}
return maxAns;
}
};
(2).动态规划
动态规划的核心思路是:若前一个元素 > 0,则将其加到数组的当前元素上,如此遍历,找到数组最大值即为最大子序和。这个思路很好理解,讲真这也是一种始终取求最优的思路,不知道与贪心法有什么区别。。。题解和代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
if (n == 1) return nums[0];
int maxSubArray = INT32_MIN ;
for (int i = 1; i < n; i++ ) {
maxSubArray = max(maxSubArray,nums[i-1]);
if (nums[i-1] > 0)
nums[i] += nums[i-1];
maxSubArray = max(maxSubArray,nums[i]);
}
return maxSubArray;
}
};
(3). 分治
分治算法我还不大懂,这里直接照搬官方解答和c++代码如下:
class Solution {
public:
struct Status {
int lSum, rSum, mSum, iSum;
};
Status pushUp(Status l, Status r) {
int iSum = l.iSum + r.iSum;
int lSum = max(l.lSum, l.iSum + r.lSum);
int rSum = max(r.rSum, r.iSum + l.rSum);
int mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum);
return (Status) {
lSum, rSum, mSum, iSum};
};
Status get(vector<int> &a, int l, int r) {
if (l == r) {
return (Status) {
a[l], a[l], a[l], a[l]};
}
int m = (l + r) >> 1;
Status lSub = get(a, l, m);
Status rSub = get(a, m + 1, r);
return pushUp(lSub, rSub);
}
int maxSubArray(vector<int>& nums) {
return get(nums, 0, nums.size() - 1).mSum;
}
};