算法导论看到了第4章的分治策略,里面讲述到了最大子序列的分治求法,下面我用代码实现出来吧:
分治法
我们先看书中给的伪代码与思想:
当最大的跨越中间的一点,分别找左右的值最大值,然后在加起来。
当不是跨越的时候使用左右各自使用分治。
代码如下:
//分治法
class Solution
{
public:
int maxSubArray(vector<int> &nums)
{
//类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
int result = INT_MIN;
int numsSize = int(nums.size());
result = maxSubArrayHelper(nums, 0, numsSize - 1);
return result;
}
int maxSubArrayHelper(vector<int> &nums, int left, int right)
{
if (left == right)
{
return nums[left];
}
int mid = (left + right) / 2;
int leftSum = maxSubArrayHelper(nums, left, mid);
//注意这里应是mid + 1,否则left + 1 = right时,会无线循环
int rightSum = maxSubArrayHelper(nums, mid + 1, right);
int midSum = findMaxCrossingSubarray(nums, left, mid, right);
int result = max(leftSum, rightSum);
result = max(result, midSum);
return result;
}
int findMaxCrossingSubarray(vector<int> &nums, int left, int mid, int right)
{
int leftSum = INT_MIN;
int sum = 0;
for (int i = mid; i >= left; i--)
{
sum += nums[i];
leftSum = max(leftSum, sum);
}
int rightSum = INT_MIN;
sum = 0;
//注意这里i = mid + 1,避免重复用到nums[i]
for (int i = mid + 1; i <= right; i++)
{
sum += nums[i];
rightSum = max(rightSum, sum);
}
return (leftSum + rightSum);
}
};
分治法我们看了后,发现它的时间复杂度是
通常我们最先想到的是暴力法,当然时间复杂度当然是最高了:
暴力解析:
代码上:
//方法1,暴力法遍历全部数据
int maxSubArray1(vector<int> & nums)
{
//类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
int max = INT_MIN;
size_t numsSize = nums.size();
for (int i = 0; i<numsSize; i++)
{
int sum = 0;
for (int j = i; j<numsSize;j++)
{
sum += nums[j];
if (sum > max)
{
max = sum;
}
}
}
return max;
}
这个就比较简单了,直接记录所有的大小,然后比较出最大值。时间复杂度就是 n^2 了
下面的方法可以解决时间复杂度使得复杂度为:n
动态规划:
int maxSubArray2(vector<int> &nums)
{
//类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
int result = INT_MIN;
size_t numsSize = int(nums.size());
//dp[i]表示nums中以nums[i]结尾的最大子序和
vector<int> dp(numsSize);
dp[0] = nums[0];
result = dp[0];
for (size_t i = 1; i < numsSize; i++)
{
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
result = max(result, dp[i]);
}
return result;
}
动态规划就是喜欢记录数据然后进行判断,比较经典的是这句代码:
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
result = max(result, dp[i]);
之前的数据 + 当前数据 如果小于 当前数据,直接就要当前的数据了。如果大于当前数据,我们继续加,要么碰到后面的当前数据比较大,要么累加的数据,都比当前的大,最后都要与result(记录的最大值做比较)。
最终可以找到最大值了,是不是非常的简单。
下面的一步大家比较熟悉的贪婪算法:
//贪心算法
int maxSubArray3(vector<int> &nums)
{
//类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
int result = INT_MIN;
int numsSize = int(nums.size());
int sum = 0;
for (int i = 0; i < numsSize; i++)
{
sum += nums[i];
result = max(result, sum);
//如果sum < 0,重新开始找子序串
if (sum < 0)
{
sum = 0;
}
}
return result;
}
喜欢的朋友可以关注加收藏。一起学习与成长。
参考连接:
算法导论的习题,现在是不是都明白了。