常见算法之《分治算法》

背景:本文以分治算法代码实例来简单梳理分治算法实现的几个关键步骤

分治算法使用条件:
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}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值