计算机执行代码时会根据输入数据的大小和算法效率来消耗一定的处理器资源。高效运行的程序需要考虑算法的效率。算法效率主要由以下两个复杂度来评估:
时间复杂度T(n):根据算法的程序执行时耗费时间的长度。
空间复杂度S(n):根据算法执行时占用存储单元的长度。
算法效率往往存在极值,即极大值和极小值,但是这些极大数据经常不贴近实际情况,没有实际意义。我们用O(x)时往往表示最小的上界(最优值, 最好的情况),Ω(x)表示最大的下界(最差值,最坏的情况)。
下列是各种模型的复杂度:
时间复杂度分析:
1)两段算法相加,复杂度等于其中最大值;
2)两端算法相乘,复杂度等于两复杂度相乘;
3)for循环的时间复杂度等于循环次数乘循环体代码的复杂度;
4)if-else结构:取每段代码中结构复杂度的最大值;
应用实例——最大子列和问题:
1、最直接、最暴力的方法就是算出所有的子列和,找其中的最大值;
int MaxSubseqSum1( int A[], int N )
{
int ThisSum, MaxSum = 0;
int i, j, k;
for( i = 0; i < N; i++ )
{ /* i是子列左端位置 */
for( j = i; j < N; j++ )
{ /* j是子列右端位置 */
ThisSum = 0; /* ThisSum是从A[i]到A[j]的子列和 */
for( k = i; k <= j; k++ )
ThisSum += A[k];
if( ThisSum > MaxSum ) /* 如果刚得到的这个子列和更大 */
MaxSum = ThisSum; /* 则更新结果 */
} /* j循环结束 */
} /* i循环结束 */
return MaxSum;
}
可知该代码的时间复杂度是O(n^3),时间成本明显很大,不是很理想;数据量n很大时很恐怖。数据量大的关键问题在于: j每加一次,ThisSum就清零重新求和,增加了额外的计算负担。
2、基于上述问题的简化:
int MaxSubseqSum1( int A[], int N )
{
int ThisSum, MaxSum = 0;
int i, j, k;
for( i = 0; i < N; i++ )
{ /* i是子列左端位置 */
ThisSum=0;
for( j = i; j < N; j++ )
{ /* j是子列右端位置 */
ThisSum += A[k];
if( ThisSum > MaxSum ) /* 如果刚得到的这个子列和更大 */
MaxSum = ThisSum; /* 则更新结果 */
} /* j循环结束 */
} /* i循环结束 */
return MaxSum;
}
可知,以上代码的执行效率是O(n^2);
3、进一步简化执行效率——分而治之:
把大问题切成小块分别解决,然后再把结果合并:
思路是:先分别在左右找最大值,最终比较得出最大值;
但是,这一定是最大值吗?万一有跨越中界限的最大子列和存在呢?
所以,最大值应该存在于这三个结果里面。跨越中线的值的情况我们采用从中线向两边扫描的方法来寻找最大子列。贴代码:
int Max3( int A, int B, int C )
{ /* 返回3个整数中的最大值 */
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer( int List[], int left, int right )
{ /* 分治法求List[left]到List[right]的最大子列和 */
int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/
int LeftBorderSum, RightBorderSum;
int center, i;
if( left == right )
{ /* 递归的终止条件,子列只有1个数字 */
if( List[left] > 0 ) return List[left];
else return 0;
}
/* 下面是"分"的过程 */
center = ( left + right ) / 2; /* 找到中分点 */
/* 递归求得两边子列的最大和 */
MaxLeftSum = DivideAndConquer( List, left, center );
MaxRightSum = DivideAndConquer( List, center+1, right );
/* 下面求跨分界线的最大子列和 */
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for( i=center; i>=left; i-- )
{ /* 从中线向左扫描 */
LeftBorderSum += List[i];
if( LeftBorderSum > MaxLeftBorderSum )
MaxLeftBorderSum = LeftBorderSum;
} /* 左边扫描结束 */
MaxRightBorderSum = 0; RightBorderSum = 0;
for( i=center+1; i<=right; i++ )
{ /* 从中线向右扫描 */
RightBorderSum += List[i];
if( RightBorderSum > MaxRightBorderSum )
MaxRightBorderSum = RightBorderSum;
} /* 右边扫描结束 */
/* 下面返回"治"的结果 */
return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}
int MaxSubseqSum3( int List[], int N )
{ /* 保持与前2种算法相同的函数接口 */
return DivideAndConquer( List, 0, N-1 );
}
4、更快的算法——在线处理法:
直接贴代码:
int MaxSubseqSum4( int A[], int N )
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for( i = 0; i < N; i++ )
{
ThisSum += A[i]; /* 向右累加 */
if( ThisSum > MaxSum )
MaxSum = ThisSum; /* 发现更大的子列和且是正值,则更新当前结果 */
else if( ThisSum < 0 ) /* 如果当前子列和为负 */
ThisSum = 0; /* 则不可能使后面的部分和增大,抛弃 */
}
return MaxSum;
}
算法描述只有一个for循环和常数级的if-else,算法复杂度为O(n);