算法導論看到了第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;
}
喜歡的朋友可以關注加收藏。一起學習與成長。
參考連接:
算法導論的習題,現在是不是都明白了。