题目大意:在一维数组的连续区间找出其总和最大的连续区间。
题目不难,但是很有启迪的一道题目。
一个了解时间复杂度题目,用几种不同的时间复杂度实现,直观的感受下几者的速度与效率。
设计一个算法时要尽量做到高效。高效之处,就是智慧之处。
先看看O(N³)的算法:
//方法MaxSum1,MaxSum2都是对数组的所有区间进行遍历求和,得出最大值
//求A[]中连续区间的最大和。O(N3)
int MaxSum1(const int A[],int len){
int ret = INT_MIN;
for(int i=0 ; i<len ; ++i){
for(int j=i ; j<len ; ++j){
int sum = 0;
//求区间A[i<-->j]的和
for(int k=i ; k<=j ; ++k){
sum+=A[k];
}
ret = max(ret,sum);
}
}
return ret;
}
//求A[]中连续区间的最大和。O(N2)
int MaxSum2(const int A[],int len){
int ret = INT_MIN;
for(int i=0 ; i<len ; ++i){
int sum = 0;
for(int j=i ; j<len ; ++j){
sum += A[j];
ret = max(ret,sum);
}
}
return ret;
}
//分治法:将数组平分为左右两个数组,则最大和区间定位于二者之一,
//或横跨两个数组,此时则可用递归或贪心来得出答案
//求A[]中连续区间的最大和。O(NlogN)
int MaxSum3(const int A[],int left,int right){
//区间的长度为1时,终止
if(left == right){
return A[left];
}
//将数组平分
int mid = (left+right)/2;
//情况1:求出横跨两个数组的最大值(一部分在左边一部分在右边)
int leftSum = INT_MIN;
int rightSum = INT_MIN;
for(int i=mid,sum=0 ; i>=left ; --i){
sum += A[i];
leftSum = max(leftSum,sum);
}
for(int i=mid+1,sum=0 ; i<=right ; ++i){
sum += A[i];
rightSum = max(rightSum,sum);
}
//情况2:最大值位于一方递归调用
int single = max(MaxSum3(A,left,mid),MaxSum3(A,mid+1,right));
//返回两种情况的最大值
return max(single,leftSum+rightSum);
}
//动态规划法
//到A[0]~A[i]的最大值,只能是A[i]本身,或到0~A[i-1]的最大值+A[i],
//所以结束与A[i]的最大值可表示为maxAt(i)=max(A[i],maxAt(i-1)+A[i])
//所以可以采用循环动态规划来实现这个状态转移方程,每步更新这个临时的最大值sum
//求A[]中连续区间的最大和。O(N)
int MaxSum4(const int A[],int len){
int ret = INT_MIN,sum=0;
for(int i=0 ; i<len ; i++){
sum = max(sum+A[i],A[i]);
ret = max(ret,sum);
}
return ret;
}
不同的时间复杂度,在处理数据大小时有天差地别,随机生成N个整数
看下几种情况下运行时间:
处理数据为10 —>好像看不出什么差别
处理数据为100 —>好像也看不出什么差别
处理数据为1000 —>O(N^3)败下阵来,差别已经出来了,我们再继续试试
处理数据为10000 —>差距已经拉开了,O(N^3)已经阵亡了
处理数据为100000 —>O(N)依旧不动,NlogN都超过1s了
以上的测试并不完全准确,跟平台机器性能有关,但至少能反映出不同时间复杂度下的处理数据的速度与大小。
综上,在编写算法时,要尽可能的优化它,想方设法完善它。多掌握一些优化技巧,可能会起到事半功倍的效果。