数据结构与算法分析:算法分析

1.数学模型

     ①4个重要的定义:如果存在正常数c和n使得N>=n时

         T\left ( N \right )\leq cf\left (N \right )  ,记作T\left ( N \right )=O\left (f\left (N \right ) \right )

         T\left ( N \right )\geq cg\left ( N \right )   ,记作T\left ( N \right )=\Omega \left ( g\left ( N \right )\right )

         当且仅当T\left ( N \right )=O \left (h \left ( N \right ) \right ) 且T\left ( N \right )=\Omega \left ( h\left ( N \right ) \right ) 有 T\left ( N \right )=\Theta \left ( h \left (N \right )\right )

         如果T\left ( N \right )=O\left ( p\left ( N \right ) \right ) 且 T\left ( N \right )\neq \Theta \left ( p\left ( N \right ) \right ) 有 T\left ( N \right )=o\left ( p\left ( N \right ) \right )

     ②

        T\left ( N \right )=O\left ( f\left ( N \right ) \right ):f(N)是 T(N)的上界

       T\left ( N \right )=\Omega \left ( f\left ( N \right ) \right ) :f(N)是T(N)的下界

    ③ 我们需要掌握的重要结论:

         法则1:如果T_{1}\left ( N \right )=O\left ( f\left ( N \right ) \right )T_{2}=O\left ( g\left ( N \right ) )

                        (a)   T_{1}\left ( N \right )+T_{2}\left ( N \right )=max\left ( O\left ( f\left ( N \right ) \right ),O\left ( g\left ( N \right ) \right ) \right )

                        (b)  T_{1}\left ( N \right )*T_{2}\left ( N \right )=O\left ( f\left ( N \right )*g\left ( N \right ) \right )

        法则2:如果T(N)是一个k次多项式,T\left ( N \right )=\Theta \left ( N^{k} \right )

        法则3:对任意常数k,\log^{k}N=O\left ( N \right )。它告诉我们对数增长得非常缓慢

        

函数 名称
c 常数
\log N 对数级
\log ^{2}N 对数平方根
N 线性级
N\log N  
N^{2} 平方级
N^{3} 立方级
2^{N} 指数级

2.要分析的问题

      影响着程序的运行时间的主要因素:所使用的算法以及对该算法的输入

     ①输入的大小

           T_{worst}\left ( N \right ):最坏情况下的运行时间(通常情况,以这个为判别算法好坏的标准)

          T_{avg}\left ( N \right ):平均运行时间

     ②最大的子序列和问题:

         给定整数A_{1}A_{2}·····,A_{N}(可能有负数),求\sum_{k=i}^{j}A_{k}的最大值。

3.运行时间的计算

    ①一般法则:从内向外展开(基本策略)

       for循环:一次for循环的时间至多是该for循环内语句的运行时间乘以迭代的次数

       嵌套的for循环:从里向外分析这些循环,运行时间为该语句的运行时间乘以改组所有for循环的大小的乘积

       顺序语句:各语句的运行时间求和

       if/else语句:不超过判断再加上if中的语句和else中语句中运行时间最长者的总的运行时间

    ②一个例子:

    long int Fib(int N){
          if(N<=1)
               return 1;
          else
               retrun Fib(n-1)+Fib(n-2);
     }

       Fib\left ( N \right )的运行时间公式:  T\left ( N \right )=T\left ( N-1 \right )+T\left ( N-2 \right )+2

       可见这个程序的运行时间以指数的速度增长:注意在调用Fib(n-1)实际上计算了Fib(n-2)。这个信息被抛弃在第二次调用时又重新计算了一遍。

       计算任何事情不要超过一次”

   ③最大子序列和问题的解

       算法1:穷举地尝试所有的可能

int MaxSequenceSum(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;
  }

      T\left ( N \right )=\sum_{i=0}^{N-1}\sum _{j=i}^{N-1}\sum _{k=i}^{j}=\frac{N^{3}+3N^{2}+2N}{6}

      算法2:撤销一个for循环避免立方运行时间

   int MaxSequenceSum(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;
  }

            T\left ( N \right )=O\left ( N^{2} \right )

       算法3:“分治”的策略。把问题分成两个大致相等的子问题,然后递归地对它们求解。这是“分”的部分。“治”阶段将两个子问题的解合并在一起并可能地做少量的附加地工作

       说明:递归过程的调用的一般形式是传递输入的数组以及左边界和有边界,它们界定了数组要处理的部分。

前半部分 后半部分
4   -3  5  -2 -1  2  6  -2

 不难得到前半部分最大子序列和为6,后半部分最大子序列和为8,横跨两部分的中间部分的最大子序列和则为11。

  static int MaxSubSum(const int A[],int Left,int Right){
        int MaxLeftSum,MaxRightSum;
        int MaxLeftBorderSum,MaxRightBorderSum;
        int LeftBorderSum,RightBorderSum;
        int Center,i;
 
        /* Base Case */
        if(Left==Right){
           if(A[Left]>0)
               return A[left];
        else 
               return 0;
         }

        Center=(Left+Right)/2;
        MaxLefeSum=MaxSubSum(A,Left,Center);
        MaxRightSum=MaxSubSum(A,Center,Right);
     
        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;
           }

        return Max3(MaxLeftSum,MaxRightSum,LeftBorderSum+RightBorderSum);
        /*     Max3返回三者中的最大数   */
}


   int MaxSequenceSum(const int A[],int N){
        return MaxSubSum(A,0,N-1);
  } 
    

得到运行时间的方程组:

                                   T\left ( 1 \right )=1

                                   T\left ( N \right )=2T\left ( \frac{N}{2}\right )+O\left ( N \right )

                                 解得T\left ( N \right )=O\left ( N\log N\right )

   算法4:只对数据进行一次扫描,一旦A[i]读入并被处理,它就不再需要被记忆

   int MaxSequenceMax(const int A[],int N){
          int ThisSum,MaxSum,j;
          ThisSum=MaxSum=0;
          for(j=0;j<N;j++){
                  ThisSum+=A[j];
                  
                  if(ThisSum>MaxSum)
                    MaxSum=ThisSum;
                  else if(ThisSum<0)
                      ThisSum=0;
           }
          return MaxSum;
  }

④运行时间中的对数

     将对数最常出现的规律归纳为下列一般法则:

        如果一个算法常用常数时间将问题的大小消减为其一部分,那么该算法就是 O\left ( \log N \right )

        如果使用常数时间把问题减少一个常数,那么该算法就是 O\left ( N \right )

        对分查找:

                         循环在High-Low=N-1开始,在High-Low>=-1时结束。每次循环后High-Low的值至少将该次循环前的值折半。于是循环的次数最多是   \left \lceil \log \left ( N-1 \right ) \right \rceil+2

  int BinarySearch(const ElementType A[],ElementType X,int N){
         int Low,Hight,Mid;
         Low=0;
         Hight=N-1;
        
         while(Low<=High){
            Mid=(Low+High)/2;
            if(A[Mid]<X)
                Low=Mid+1;
            else if([Mid]>X)
                 High=Mid-1;
            else 
                 return Mid;
          }
          return NotFound;
  }

        欧几里得算法:计算最大公因子

                                    我们可以证明,在两次迭代后,余数最多是原始值的一半。

                                     T\left ( N \right )=O\left ( \log N \right )

  unsigned ind Gcd(unsigned int M,unsigned int N){
      unsigned int Rem;
      
      while(N>0)
    {  
       Rem=M%N;
       M=N;
       N=Rem;
    }

     return M;
  }

   定理2:

        如果M>N,则M mod N < M/2;

   幂运算:

          计算X^{N}最常见的方法就是使用N-1次乘法自乘。

  long int Pow(long int X,unsigned int N){
            if(N==0)
                 return 1;
            if(N==1)
                 return X;
            if(IsEven(X)) 
                 return Pow(X*X,N/2);
            else 
                 return Pow(X*X,N/2)*X;
          }

⑤ 检验你的分析

⑥分析结果的准确性

猜你喜欢

转载自blog.csdn.net/rachel9798/article/details/82806485