背景:本文以分治算法代码实例来简单梳理分治算法实现的几个关键步骤
分治算法使用条件:
1、能拆分成子问题,且子问题小到一定规模是容易解的
2、子问题的解直接合并可以得到原问题的解
原则上满足以上两个条件即可用分治算法。
如果子问题之间相互独立则考虑分治算法,不独立则考虑用能否满足动态规划算法条件,动态规划可提高算法效率,因为动态规划算法可以保存子问题结果,减少计算。
编程题为 leetcode “连续数列”:
给定一个整数数组,找出总和最大的连续数列,并返回总和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/contiguous-sequence-lcci
完整代码如下:
// 分治法
int maxSubArray(vector<int>& nums)
{
if(nums.size() == 0) return INT_MIN;
return divide(nums,0,nums.size()-1);
}
int divide(vector<int>& nums, int left, int right)
{
if(left == right) return nums[left];
int mid = (left + right) / 2;
// 1. 最大数列和在左边
int sumLeft = divide(nums,left,mid);
// 2. 最大数列和在右边
int sumRight = divide(nums,mid+1,right);
// 3. 最大数列和在中间
// 先求左边的最大和
int leftSum = 0,leftMaxSum = INT_MIN;
for(int i = mid; i >= left; i--)
{
leftSum += nums[i];
leftMaxSum = max(leftMaxSum,leftSum);
}
// 求右边的最大和
int rightSum = 0,rightMaxSum = INT_MIN;
for(int i = mid + 1; i <= right; i++)
{
rightSum += nums[i];
rightMaxSum = max(rightMaxSum,rightSum);
}
return max(max(sumLeft,sumRight),leftMaxSum+rightMaxSum);
}
作者:yang-zi-ju-2
链接:https://leetcode-cn.com/problems/contiguous-sequence-lcci/solution/fen-zhi-he-dong-tai-gui-hua-liang-chong-jie-fa-by-/
来源:力扣(LeetCode)
分治算法通用算法公式:
1、子问题达到一定规模时容易解,直接返回解
if(left == right)
return nums[left];
2、子问题划分,一般是二分法
int mid = (left + right) / 2;
// 1. 最大数列和在左边
int sumLeft = divide(nums,left,mid);
// 2. 最大数列和在右边
int sumRight = divide(nums,mid+1,right);
3、子问题合并成原问题的解
return max(max(sumLeft,sumRight),leftMaxSum+rightMaxSum);
因为合并时还要考虑解可能是跨越左右子数组的,所以加上计算跨越左右子数组的最大值
int leftSum = 0,leftMaxSum = INT_MIN;
for(int i = mid; i >= left; i--)
{
leftSum += nums[i];
leftMaxSum = max(leftMaxSum,leftSum);
}
// 求右边的最大和
int rightSum = 0,rightMaxSum = INT_MIN;
for(int i = mid + 1; i <= right; i++)
{
rightSum += nums[i];
rightMaxSum = max(rightMaxSum,rightSum);
}
再梳理一下伪代码,一般函数入参为3个,第一个是原问题数据,第二和第三分别是子问题边界
int divide(vector<int>& input, int left, int right) {
// 子问题达到一定规模时容易解,直接返回解
if (可解子问题边界条件) {
return 解;
}
// 子问题划分,一般是二分法
int leftAns = divide(input, left, (left+right)/2);
int rihgtAns = divide(input, (left+right)/2 + 1, right);
int otherAns = ...; //其他可能最有解
// 子问题合并成原问题的解
return 选最优{leftAns, rihgtAns, otherAns}
}