最大子序列和问题
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
这是一道经典的算法题,在LeetCode上的编号是53。
本文以这道题为例学习分治算法
分治算法
分治算法的核心是把问题分成两个大致相等的子问题,然后递归对它们求解,这是“分”的部分,在“治”这一阶段将两个子问题的解合并到一起求解。
根据算法的思想,把数组分割成两部分,左半部分和右半部分,最大子序列出现的位置可能在:
- 左半部
- 右半部
- 跨越分割的位置占据左右半部
递归是这个算法里非常重要的一个环节,它把数组划分到最小单元来进行比较
1:[4,-3,5,-2,-1,2,6,-2]
2:[4,-3,5,-2] [-1,2,6,-2]
3:{[4,-3] [5,-2]} {[-1,2] [6,-2]}
把数组分成了四份,每一份只有两个元素。计算的过程是从左到右进行,比较左边元素,右边元素和两个元素之和的大小,取最大值,也就是max(4,-3,(4+(-3)))
,结果是4。同理,整个数组的左半部分最大值是6,最大子序列就是4,-3,5
。
[4,-3]=4 [5,-2]=5
[4,-3,5,-2]=6 max(4,5,6)=6
[-1,2]=2 [6,-2]=6
[-1,2,6,-2]=8 max(2,6,8)=8
[4,-3,5,-2,-1,2,6,-2]=11
max(6,8,11)=11
算法实现
下面是用JavaScript实现的分治算法实现
/**
* 求最大子序列和
*
* @param {*} array 目标数组
* @param {*} left 左边界
* @param {*} right 右边界
* @returns
*/
function maxSubSum(array,left,right){
var maxLeftSum,maxRightSum
var maxLeftBorderSum,maxRightBorderSum
var leftBorderSum,rightBorderSum
var center
if(left===right){//基准情形
if(array[left]>0){
return array[left]
}
else{
return 0
}
}
center=parseInt((left+right)/2)
maxLeftSum=maxSubSum(array,left,center)
maxRightSum=maxSubSum(array,center+1,right)
maxLeftBorderSum=0
leftBorderSum=0
for (let i = center; i >=left; i--) {
leftBorderSum+= array[i];
if(leftBorderSum>maxLeftBorderSum){
maxLeftBorderSum=leftBorderSum
}
}
maxRightBorderSum=0
rightBorderSum=0
for (let i = center+1; i <=right; i++) {
rightBorderSum+=array[i]
if(rightBorderSum>maxRightBorderSum){
maxRightBorderSum=rightBorderSum
}
}
var maxSum=max3(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorderSum)
return maxSum
}
function max3(a,b,c){
var max=a>b?a:b
max=max>c?max:c
return max
}
maxSubSum([4,-3,5,-2,-1,2,6,-2],0,7);
算法复杂度分析
设是求解大小为N的最大子序列和问题所花费的时间。
- 如果,则不用进行递归,只用进行一次简单的比较即可,花费一个时间单元, 这样可直接得出结果,记作。
- 如果。先讨论求解maxLeftSum和maxRightSum的两个递归调用,这两行求解大小为的子序列问题(假设N是偶数),也就是说每个递归各涉及一半的元素,对每个元素都要进行和上一条一样时间的计算,所以每行花费的时间是个时间单位,两个递归加起来就是个时间单位。再看后面的两个for循环,这俩for循环会接触数组中的每一个元素,但循环内部的工作量是常数,时间复杂度为。
经过分析得到两个方程组
为了简化计算,设置两个前提:
- 假设N是2的幂,也就是说我们总是可以把数组分割成包含偶数个子元素的两部分 。
- 用N代替上面的O(N),因为T(N)最终是用O(N)来表示的,所以这样做不影响最后的结果。
得到方程。
两边同时除以,得到:
这个方程对于任意2的幂都成立,所以下面的方程都是正确的
一共有个方程,所有方程两边相加,消去相同项后得到:
得到最终的结果:
以上的分析基于是2的幂这个假设,如果不满足,方程不成立;当不是2的幂时,需要加入一些复杂的分析,但是大的结果不变。