在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。

   算法的时间复杂度,也就是算法的时间量度,记作:T(n) = O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,即T(n)和f(n)是同一个数量级的,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。

   我们常用“大O记法”来表示时间复杂度,这里的“O”指的是量级(order)。

   那如何分析一个算法的时间复杂度?如何推导出大O阶呢?下面给出基本方法:

   推导大O阶:

   1)用常数1取代运行时间中的所有加法常数。

   2)在修改后的运行次数函数中,只保留最高阶项。

   3)如果最高阶项存在且不是1,则去除与这个项想乘的常数。

   下面用几个具体的例子来理解一下大O推导的过程。

   1)O(1)

      int sum = 0, n = 100;      //执行1次

      sum = (1 + n) * n / 2;     //执行1次

      printf("%d", sum);         //执行1次

      算法运行次数函数f(n) = 3,根据大O推导的过程,第一步将3改为1;第二步,由于没有最高阶项所以算法的时间复杂度为O(1)。

      再举一个例子:

     

       算法运行次数函数f(n) = 12,根据大O推导的过程,第一步将12改为1;第二步,由于没有最高阶项所以算法的时间复杂度还是为O(1)。

       由此可以看出该算法的时间复杂度不会随着规模n的变大而变大,即使算法有上千条语句,执行时间也是一个较大的常数项,时间复杂度还是O(1)。

   2)O(n)

      int i;                  //执行1次

      for(i = 0; i < n; i++)  //执行n次

      {

          printf("%d ", i);   //执行n次

      }

      对于这种带有循环结构的算法,主要还是看循环语句执行的次数。对于该算法,算法运行次数函数f(n) = 2n + 1,根据大O推导的过程,第一步直接过,第二步只保留最高阶项即2n,第三步去除最高阶项的系数2,结果为n,所以时间复杂度为O(n)。

   3)O(n^2)

      int i, j;                    //执行1次

      for(i = 0; i < n; i++)       //执行n次

          for(j = 0; j < n; j++)   //执行n^2次

          {

               printf("hello\n");  //执行n^2次

           }

       对于该算法,算法运行次数函数f(n) = 2n^2 + n + 1,根据大O推导的过程,第一步直接过,第二步只保留最高阶项2n^2,第三步去除最高阶项的系数2,结果为n^2,所以时间复杂度为O(n^2)。

      如果将代码改为:      

      int i, j;                    //执行1次

      for(i = 0; i < n; i++)       //执行n次

          for(j = 0; j < m; j++)   //执行n * m次

          {

               printf("hello\n");  //执行n * m次

           }

       那该算法的时间复杂度为O(n * m).

       如果将代码改为:

       int i, j;                    //执行1次

       for(i = 0; i < n; i++)       //执行n次

          for(j = i; j < n; j++)   //执行n + (n - 1) +......+ 1次

          {

               printf("hello\n");   //执行n + (n - 1) +......+ 1次

           }

       对于该算法,算法运行次数函数f(n) = n * (n + 1) + n + 1 = n^2 + 2n + 1,根据大O推导的过程,第一步直接过,第二步只保留最高阶项n^2,第三步过,结果为n^2,所以时间复杂度还是为O(n^2)。

   4)O(logn)

      int i = 1;          //执行1次

      while(i < n) {      //执行log(2)n次,2为底数,下同

         i *= 2;          //执行log(2)n次

      }

      对于log(2)n是怎么求出来的,我们可以假设i *= 2;语句执行了x次,现在每执行一次,i就会乘以2,知道i >= n。所以2^x = n,x = log(2)n。

      对于该算法,算法运行次数函数f(n) = 2log(2)n + 1,根据大O推导的过程,第一步直接过,第二步只保留最高阶项2log(2)n,第三步去除最高阶项的系数2,结果为log(2)n,所以时间复杂度为O(log(2)n)。对于底数2,不是确定的,还要看具体的算法,因为如果算法改为i *= 3的话,时间复杂度为O(log(3)n)了。

     还有很多种时间复杂度,对于具体算法还是要具体分析,这里只是介绍了如何去分析出算法的时间复杂度,下面附上常见的算法复杂度。