7-1 最大子列和问题
给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1。“最大子列和”则被定义为所有连续子列元素的和中最大者。
例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
问题应这样解释:从给定一个数组中 ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),然后返回其最大和(应该是要大于等于零)。
int findmax(int *a,int left,int right) { if(left==right) return a[left]; int mid=(left+right)/2; int lmax=findmax(a,left,mid); int rmax=findmax(a,mid+1,right); int add=0,addl=a[mid],addr=a[mid+1]; for(int i=mid;i>=left;i--) { add+=a[i]; if(add>addl) addl=add; } add=0; for(int i=mid+1;i<=right;i++) { add+=a[i]; if(add>addr) addr=add; } return maxnum(addl+addr,addl,addr); }
函数 findmax 是核心递归代码,利用了递归分治的思想,每次递归进行三大步的操作,首先求出中点值,并从当前中心点向左搜索找出最大子序列和,然后从当前中心点向右搜索找出最大子序列和,最后以中心点下标分别向右和向左遍历找出最大子序列和,上述三者的最大值作为本层递归的返回值给上一层递归进行调用。实质上是从大(范围)到小(递归分解),再由小到大(返回求和)。
每次将问题分解为两个子问题,每个子问题的长度为原问题的一半,递归本身的时间复杂度最坏情况下应为为O(log2 n),最后一次找到要找的数;
对于n个元素的情况:
第一次二分:n/2
第二次二分:n/2^2= n/4
......
m次二分:n/(2^m)
在最坏情况下是在排除到只剩下最后一个值之后得到结果,所以为
n/(2^m)=1;
2^m=n;
第二次二分:n/2^2= n/4
......
m次二分:n/(2^m)
在最坏情况下是在排除到只剩下最后一个值之后得到结果,所以为
n/(2^m)=1;
2^m=n;
且每次递归中,有有限个O(1)操作,有两个循环体,一个是从头到中间,另外一个是从中间到尾部, 所以加起来时间复杂度就是 O(n)。
所以时间复杂度为:O(n*logn);
空间复杂度为O(1);
心得:这道题比较经典,能使用分治法的题目也是万变不离其宗,先要思考好若是使用递归算法,返回什么值,分治操作范围是由大到小(求精确值),还是先由大到小再由小到大(求和),每次递归中有哪些步骤,这些步骤的先后顺序,以及递归调用的参数如何设置,考虑好这些,使用分治法处理一些比较简单的问题就能很快的梳理好算法思想。