【数据结构与算法】第二章:算法分析

本文深入讲解了算法分析的基础知识,包括大O记法在内的四种基本记号及其应用法则,介绍了几种典型的算法案例如最大子序列和问题的不同解决方法,探讨了不同算法的时间复杂度,并分析了对数时间复杂度的由来。

【数据结构与算法】第二章:算法分析


第二章:算法分析

2.1数学知识预备

  • 四个定义:

    1. O O 记法:如果存在正常数 c c n0使得当 Nn0 N ⩾ n 0 T(N)cf(N) T ( N ) ≤ c f ( N ) ,则记为 T(N)=O(f(N)) T ( N ) = O ( f ( N ) ) .
    2. 如果存在正常数 c c n0使得当 Nn0 N ⩾ n 0 T(N)cg(N) T ( N ) ≥ c g ( N ) ,则记为 T(N)=Ω(f(N)) T ( N ) = Ω ( f ( N ) ) .
    3. T(N)=Θ(H(N)) T ( N ) = Θ ( H ( N ) ) ,当且仅当 T(N)=O(f(N)) T ( N ) = O ( f ( N ) ) T(N)=Ω(f(N)) T ( N ) = Ω ( f ( N ) ) .
    4. 如果 T(N)=O(f(N)) T ( N ) = O ( f ( N ) ) T(N)Θ(f(N)) T ( N ) ≠ Θ ( f ( N ) ) ,则记作 T(N)=o(f(N)) T ( N ) = o ( f ( N ) ) .
  • 在大 O O 记法中, T(N) T ( N ) f(N) f ( N ) 的一个下界, f(N) f ( N ) T(N) T ( N ) 的一个上界.

  • 性质:

    1. 如果 T1(N)=O(f(N)) T 1 ( N ) = O ( f ( N ) ) T2(N)=O(g(N)) T 2 ( N ) = O ( g ( N ) ) 那么a) T1(N)+T2(N)=max(O(f(N)),O(g(N))) T 1 ( N ) + T 2 ( N ) = m a x ( O ( f ( N ) ) , O ( g ( N ) ) ) b) T1(N)T2(N)=O(f(N)g(N)) T 1 ( N ) ∗ T 2 ( N ) = O ( f ( N ) ∗ g ( N ) )
    2. 如果 T(N) T ( N ) 是一个 k k 次多项式,那么有 T(N)=Θ(Nk) T ( N ) = Θ ( N k ) .
    3. 对于任意常数k, logkN=O(N) l o g k N = O ( N ) .
  • 常见增长率

函数名称
c c 常数
c c 常数
logN l o g N 对数
log2N l o g 2 N 对数平方根
N N 线性
NlogN N l o g N
log2N l o g 2 N 对数平方根

2.2模型

  1. 假定模型机做任何一件简单的工作都恰好花费一个时间单元.
  2. 为合理起见,我们假定我们的模型都有固定范围的整数,并且不存在矩阵求逆或排序等运算.
  3. 假定模型机有无限的内存.

2.3要分析的问题

  • 运行时间
    1. 平均时间: Tavg(N) T a v g ( N )
    2. 最坏时间: Tworst(N) T w o r s t ( N )

注,一般而言 Tavg(N)Tworst(N) T a v g ( N ) ≤ T w o r s t ( N )

2.4运行时间计算

2.4.1 一般法则

  1. FOR循环:运行时间至多是该循环内语句的运行时间乘上迭代次数.
  2. 嵌套的FOR循环:在嵌套的循环内部的一条语句的总的运行时间是该语句乘以该组所有的for循环的大小的乘积.
  3. 顺序语句:将各个语句的运算时间求和.
  4. if-else语句:不超过判断加分支中运算时间长者的总的运行时间.

2.4.2 例题

  • 最大子序列和问题

    给定正整数 A1,A2,...AN A 1 , A 2 , . . . A N jk=iAk ∑ k = i j A k 的最大值(若所给的整数均为负数,则最大子序列的和为0).

    输入样例: -2, 11, -4, 13, -5, -2
    输出结构: 20 (从 A2 A 2 A4 A 4 )


/* 算法1:穷举法 */

int MaxSubsequenceSum_1( const int A[], int N){
    int ThisSum, MaxSum, i, j, k;
    MaxSum = 0;
    for( i=0; i<N; i++){
        for( j=i; j<N; j++){
            ThisSum = 0;
            for( k=i; k<=j; k++)
                ThisSum += A[k];
            if( ThisSum > MaxSum)
                MaxSum = ThisSum;
        }
    }
    return MaxSum;
}
/* 算法2:算法1的改进版 */

int MaxSubsequenceSum_2( const int A[], int N){
    int ThisSum, MaxSum, i, j;
    MaxSum = 0;
    for( i=0; i<N; i++){
        ThisSum = 0;
        for( j=i; j<N; j++){
            ThisSum += A[j];
            if( ThisSum > MaxSum)
                MaxSum = ThisSum;
        }
    }
    return MaxSum;
}
/* 算法3:分治策咯 */

int MaxSubsequenceSum_3( const int A[], int Left, int Right){
    int MaxLeftSum, MaxRightSum;
    int MaxLeftBorderSum, MaxRightBorderSum;
    int LeftBorderSum, RightBorderSum;
    int Center, i;

    if( Left == Right){     //Base Case
        if( A[Left] > 0)
            return A[Left];
        else
            return 0;
    }

    Center = ( Left + Right) /2;
    MaxLeftSum = MaxSubsequenceSum_3( A, Left, Center);
    MaxRightSum = MaxSubsequenceSum_3( A, Center + 1, Right);
    //Both Side MaxSum
    MaxLeftBorderSum = 0; LeftBorderSum = 0;
    for( i = Center; i >= Left; i--){
        LeftBorderSum += A[i];
        if( LeftBorderSum > MaxLeftBorderSum)
            MaxLeftBorderSum = LeftBorderSum;
    }

    MaxRightBorderSum = 0; RightBorderSum = 0;
    for( i = Center + 1; i <= Right; i++){
        RightBorderSum += A[i];
        if( RightBorderSum > MaxRightBorderSum)
            MaxRightBorderSum = RightBorderSum;
    }

    int SideMaxSum = MaxLeftSum > MaxRightSum ? MaxLeftSum : MaxRightSum;
    int BorderMaxSum = MaxRightBorderSum + MaxLeftBorderSum;
    return  SideMaxSum > BorderMaxSum ? SideMaxSum : BorderMaxSum;
}
/* 算法4 */

int MaxSubsequenceSum_4( const int A[], int N){
    int ThisSum, MaxSum, 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;
}

2.4.3 运行时间中的对数

  • 对数出现的一般法则:

如果一个算法用常数时间 O(1) ( O ( 1 ) ) 将问题的大小削减为其一部分(通常是 12) 1 2 ) ,那么该算法的时间复杂度就是 O(logN) ( O ( l o g N ) ) .另一方面,如果使用常数时间只是把问题减少一个常数 O(1) ( 比 如 O ( 1 ) ) ,那么这个算法就是 O(1) ( O ( 1 ) ) 的.

  • 对分查找
    给定一个整数 X X 和整数 A1,A2,...AN A 1 , A 2 , . . . A N ,后者已经预先排序并在内存中,求使得 Ai=X A i = X 的下标 i i ,如果X不在数据中,则返回 i=1 i = 1 .

  • 最简单的便是线性查找,时间复杂度为 O(N) ( O ( N ) ) ,

  • 对分查找
int BinarySearch( int A[], int X, int N){
    int Low, Mid, High;
    Low = 0;
    High = N - 1;
    while( Low <= High){
        Mid = (Low + High) / 2;
        if( A[Mid] == X)
            return Mid;
        else if( A[Mid] < X)
            Low = Mid + 1;
        else if( A[Mid] > X)
            High = Mid - 1;
    }
    return NotFound;
}
  • 欧几里得算法
    欧几里得算法是计算最大公因数的算法.两个数的最大公因数Gcd是同时整除二者的最大整数.例如Gcd(50,15)=5.
    对于正整数M,N.假设M>N则必然有 M mod N < M/2.
//假设M>N
unsigned int Gcd( unsigned int M , unsigned int N){
    unsigned int Rem;
    while( N > 0){
        Rem = M % N;
        M = N;
        N = Rem;
    }
    return M;
}
  • 幂运算
    假设机器的内存足够大.计算 XN X N 的一般方法是使用N-1次乘法.
long int Pow( long int X, unsigned int N){
    if( N == 0)
        return 1;
    if( N == 1)
        return X;
    if( N % 2 == 0) //X是偶数
         return Pow( X*X, N/2);
    else
         return Pow( X*X, N/2) * X;
}
我的微信公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值