最大子数组问题
2013-12-12 00:34
106 查看
最大子数组问题就是求数组中和最大的非空连续子数组。比如13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7的最大子数组就是18, 20, -7, 12。下面分别用三种方法来求解此问题。
定义全局最大子数组结构体:
方法一:暴力求解,时间复杂度O(n2)
此方法是求出每一个子数组的和,然后取最大值,从而得出结果。
代码如下:
方法二:分治求解,时间复杂度O(nlgn)
我们可以这样理解此问题,在数组A[low:mid:high]中,最大子数组要么存在于A[low:mid],要么存在于A[mid+1:high],要么跨越mid存在于A[i:mid:j],其中(low<=i<mid, mid<j<=high)。因此,只要取以上三种情况的最大值即可得出答案。
代码如下:
方法三:顺序扫描,时间复杂度O(n)
此方法的原理就是:从数组的左边界开始,从左至右记录到目前为止已经处理的最大子数组;已知A[1:j]的最大子数组,那么A[1:j+1]的最大子数组要么就是A[1:j]的最大子数组,要么就是A[i:j+1](1=<i<=j+1)。具体实现如下:
在数组A[1:n]中,初始时,设最大子数组的左边界max_left右边界max_right都为1,和max_sum=A[1];
sum累加i(初始为1)到j+1的和(1=<i<=n),如果sum小于0,则新的最大子数组的新左边界应从j+2开始,i置j+2,sum置为A[j+2];
如果sum大于A[1:j]的最大子数组的和max_sum,则更新最大子数组的边界,即max_left=i,max_right=j+1,同时更新max_sum=sum。
代码如下:
定义全局最大子数组结构体:
//最大子数组结构 struct MaxSubArray { int max_left; //最大子数组左边界 int max_right;//最大子数组右边界 int max_sum;//最大子数组和 MaxSubArray(int maxLeft, int maxRight, int maxSum) :max_left(maxLeft), max_right(maxRight), max_sum(maxSum){} };
方法一:暴力求解,时间复杂度O(n2)
此方法是求出每一个子数组的和,然后取最大值,从而得出结果。
代码如下:
暴力解法,求出每个子数组的和,取最大值 MaxSubArray findMaxSubarray(int A[], int n) { int max_left = 0, max_right = 0, max_sum = A[0]; int sum = 0; for (int i=0; i<n; i++)//每个i开始的子数组 { sum = 0; //保存子数组A[i:j-1]的和; for (int j=i; j<n; j++)//产生A[i:j]的子数组 { if ((sum += A[j]) > max_sum) //更新 { max_sum = sum; max_left = i; max_right = j; } } } return MaxSubArray(max_left, max_right, max_sum); }
方法二:分治求解,时间复杂度O(nlgn)
我们可以这样理解此问题,在数组A[low:mid:high]中,最大子数组要么存在于A[low:mid],要么存在于A[mid+1:high],要么跨越mid存在于A[i:mid:j],其中(low<=i<mid, mid<j<=high)。因此,只要取以上三种情况的最大值即可得出答案。
代码如下:
//求跨越数组A[low:high]中间位置mid的最大子数组 MaxSubArray findMaxCrossSubArray(int A[], int low, int mid, int high) { int max_left=mid, left_sum = A[mid]; int sum = 0; for (int i=mid; i>=low; i--) { if((sum+=A[i]) > left_sum) { left_sum = sum; max_left = i; } } int max_right = mid+1, right_sum = A[mid+1]; sum = 0; for (int j=mid+1; j<=high; j++) { if ((sum += A[j]) > right_sum) { right_sum = sum; max_right = j; } } return MaxSubArray(max_left, max_right, left_sum+right_sum); } //分治法求A[low:high]的最大子数组 MaxSubArray findMaxSubarray(int A[], int low, int high) { if(low>=high) //少于一个元素 return MaxSubArray(low, low, A[low]); else { int mid = (high+low)/2; MaxSubArray left = findMaxSubarray(A, low, mid); //求左边的最大子数组 MaxSubArray right = findMaxSubarray(A, mid+1, high); //求右边的最大子数组 MaxSubArray cross = findMaxCrossSubArray(A, low, mid, high);//求跨越mid的最大子数组 if(left.max_sum >= right.max_sum && left.max_sum >= cross.max_sum) return left; else if(right.max_sum >= left.max_sum && right.max_sum >= cross.max_sum) return right; else return cross; } }
方法三:顺序扫描,时间复杂度O(n)
此方法的原理就是:从数组的左边界开始,从左至右记录到目前为止已经处理的最大子数组;已知A[1:j]的最大子数组,那么A[1:j+1]的最大子数组要么就是A[1:j]的最大子数组,要么就是A[i:j+1](1=<i<=j+1)。具体实现如下:
在数组A[1:n]中,初始时,设最大子数组的左边界max_left右边界max_right都为1,和max_sum=A[1];
sum累加i(初始为1)到j+1的和(1=<i<=n),如果sum小于0,则新的最大子数组的新左边界应从j+2开始,i置j+2,sum置为A[j+2];
如果sum大于A[1:j]的最大子数组的和max_sum,则更新最大子数组的边界,即max_left=i,max_right=j+1,同时更新max_sum=sum。
代码如下:
MaxSubArray findMaxSubarray(int A[], int n) { int max_left = 0, max_right = 0, max_sum = A[0]; int i = 0, sum = 0; for (int j=0; j<n; j++) { if (sum < 0) { sum = A[i];//A[max_left:i-1]之和小于0,不应再考虑,重新从i开始 i = j; } else sum += A[j]; if(max_sum < sum) //新的和,更新右边界 { max_left = i; max_right = j; max_sum = sum; } } return MaxSubArray(max_left, max_right, max_sum); }
相关文章推荐
- 分治法解决最大子数组问题 (C/C++)
- 最大子数组问题
- 子数组和最大值,以及不相交子数组其和之差的最大值问题
- 算法导论-最大子数组问题-线性时间复杂度算法分析与实现
- 最大子数组问题(动态规划)--【算法导论】
- [置顶] 算法导论学习:分治策略之最大子数组问题
- 算法导论3:最大子数组问题 2016.1.3
- 连续子数组最大和问题
- 动手才会属于自己--求最大子数组问题拓展
- 数组与矩阵---子矩阵的最大累加和问题
- 编程之美2.14扩展问题1 求子数组和的最大值(首尾可以相连)
- 堆栈溢出问题 调试到位置(test dword ptr [eax],eax ; probe page.)局部数组变量定义所分配的最大空间为1M
- java算法7~求子数组的最大累加和问题
- 【软件工程】代码复审与子数组最大和线性算法寻找问题
- 编程之美--求数组的子数组之和的最大值--扩展问题
- 结对开发之求最大数组溢出问题
- 求连续子数组的最大和问题
- 用分治法实现最大子数组问题(Java)
- 分治法解决最大子数组问题
- 最大子数组问题