递归解法
递归解法,开始解法存在超时,优化方法是将vector的长度放在类成员中或通过参数传递,如果在helper中每次求nums.size()会导致运行超时。
class Solution {
private:
//目标和个数
int count;
//vector大小,减少递归函数里查询O(n)复杂度
int size;
public:
int findTargetSumWays(vector<int>& nums, int S) {
size = nums.size();
helper(nums,0,0,S);
return count;
}
private:
void helper(const vector<int>& nums,int len,int sum,int S){
if(len == size)
{
if(sum == S)
count++;
return;
}else{
helper(nums,len+1,sum+nums[len],S);
helper(nums,len+1,sum-nums[len],S);
}
}
};
运行效率:
Runtime: 1120 ms, faster than 19.53% of C++ online submissions for Target Sum.
Memory Usage: 8.9 MB, less than 98.13% of C++ online submissions for Target Sum.
递归加记忆化
递归之所以执行效率比较低是因为存在大量的重复计算,所以可以使用记忆化来解决这个问题。
class Solution {
private:
int target;
int vec_size;
public:
int findTargetSumWays(vector<int>& nums, int S) {
target = S;
vec_size = nums.size();
vector<unordered_map<int,int>> mem(nums.size());
return helper(nums,0,0,mem);
}
private:
int helper(const vector<int> & nums,int loc,int sum,vector<unordered_map<int,int>> &mem) {
if(loc == vec_size)
return sum == target;
auto iter = mem[loc].find(sum);
if(iter != mem[loc].end()) return iter->second;
mem[loc][sum] = helper(nums,loc+1,sum+nums[loc],mem) + helper(nums,loc+1,sum-nums[loc],mem);
return mem[loc][sum];
}
};
运行效率:
Runtime: 216 ms, faster than 36.79% of C++ online submissions for Target Sum.
Memory Usage: 61.2 MB, less than 9.71% of C++ online submissions for Target Sum.
动态规划解法
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
unordered_map<int,int> cur{{0,1}},next;
for(int i = 0;i < nums.size();i++)
{
for(auto& it : cur)
{
next[it.first + nums[i]] += it.second;
next[it.first - nums[i]] += it.second;
}
swap(cur,next);
next.clear();
}
return cur[S];
}
};
运行效率:
Runtime: 140 ms, faster than 45.56% of C++ online submissions for Target Sum.
Memory Usage: 35.4 MB, less than 30.00% of C++ online submissions for Target Sum.
这里的dp解法与之前的解法不同,这里向后递推不是线性关系,比如我知道了dp[n],可以求解dp[n+1],这道题的解法我理解由初始状态向后扩展,以官方例子Input: nums is [1, 1, 1, 1, 1], S is 3.为例:
cur = 0 next 1 -1
---------
cur = -1 next 0 -2
cur = 1 next 2 0
---------
cur = 2 next 3 1
cur = 0 next 1 -1
cur = -2 next -1 -3
---------
cur = -3 next -2 -4
cur = -1 next 0 -2
cur = 1 next 2 0
cur = 3 next 4 2
---------
cur = 0 next 1 -1
cur = 2 next 3 1
cur = -4 next -3 -5
cur = 4 next 5 3
cur = -2 next -1 -3
---------
开始时我们只知道{0,1},然后+1 和-1,这样我们就知道了要组成1和-1需要的组合个数,接着对1和-1同样加上当前当前元素nums[i],这个例子中比较特殊所有元素都是1,每次swap(cur,next),然后清空掉next.