浅析时间复杂度与空间复杂度

            首先提出一个问题,如何衡量一个算法的复杂度?
             算法的时间复杂度空间复杂度统称为算法的复杂度。

一.时间复杂度

     1.概念:

       一般情况下,算法中本操作重复执行的次数是问题规模n的某个函数f(n),进而分析f(n)随n的变化情况并确定T(n)的数量级。这里用"O"来表示数量级,算法的时间复杂度表示为:T(n)=O(f(n));这个表达式表示随着问题规模的n的增大,算法的执行时间的增长率和f(n)的增长率相同,这称作算法的渐进时间复杂度,简称时间复杂度。而我们一般讨论的是最坏时间复杂度,这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,分析最坏的情况以估算算法指向时间的一个上界。

   简单来说,时间复杂度实际就是一个函数,该函数计算的是执行基本操作的次数。

     2.计算时间复杂度的方法:

  <1> 用常数1代替运行时间中的所有加法常数

  <2>修改后的运行次数函数中,只保留最高阶项

  <3>如果最高项系数存在且不为1,去除最高阶项的系数

     3.按数量级递增排列,常见的时间复杂度有: 

         常数阶O(1),对数阶O(log2n),线性阶O(n)线性对数阶O(nlog2n),平方阶O(n^2),立方阶O(n^3),…, 

 k次方阶O(n^k),指数阶O(2^n)。  

   随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。

  

  二.空间复杂度   

1.概念:

扫描二维码关注公众号,回复: 1593507 查看本文章

   空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记为S(n)=O(f(n))。它是指运行完一个程序所需内存的大小

   对于一个算法来说,空间复杂度和时间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。有时我们可以用空间来换取时间以达到目的。

2.程序执行时所需存储空间包括以下两部分  

    <1>固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。

    <2>可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。

3.递归算法的空间复杂度:

   递归深度N*每次递归所要的辅助空间 (如果每次递归所需的辅助空间是常数,则递归的空间复杂度是 O(N)).

三.举例说明时间复杂度和空间复杂度

  1.普通情况

        <1>

x=100;
y=60;
while(y>0) 
if(x>10) 
{
   x=x-3;
   y--;
} 
else
    x++;
解答: T(n)=O(1)

这个程序看起来循环了很多次,但是并没有看到n,故这段程序的运行是和n无关的,只是一个常数阶的函数。
如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。
此类算法的时间复杂度是O(1)

<2>

for(int i = 0; i < n; i++){
    printf("%d ",i);
}
//运行次数n                       
//时间复杂度O(n)

<3>

for(int i = 0; i < n; i++){
    for(int j = 0; j < n; j++){
        printf("%d ",i);
    }
}
//运行次数n^2,时间复杂度O(n^2)

<4>

for(int i = 0; i < n; i++){
    for(int j = i; j < n; j++){
        printf("%d ",i);
    }
}   
//运行次数为(1+n)*n/2
//时间复杂度O(n^2)

<5>

int i = 1, n = 100;
while(i < n){
    i = i * 2;
}
//设执行次数为x. 2^x = n 即x = log2n
//时间复杂度O(log2n)

2.求二分法的时间复杂度和空间复杂度。

<1>非递归:

#include<stdio.h>
#include<assert.h>
{  
       assert(number>=0);  
       int left = 0;  
       int right = number-1;  
       while (right >= left)  
       {  
              int mid = (left&right) + ((left^right)>>1);  
              if (array[mid] > data)  
              {  
                     right = mid - 1;  
              }  
              else if (array[mid] < data)  
              {  
                     left = mid + 1;  
              }  
              else  
              {  
                     return (array + mid);  
              }  
       }  
       return NULL;  
}  

分析如下


 假设最坏的情况下,循坏x次找到,有n/(2^x)=1,则x=log2 n

 循环的基本次数是log2 n,所以:

 时间复杂度是O(log2 n);

 由于辅助空间是常数级别的所以:
 空间复杂度是O(1);

 <2> 递归:

#include<stdio.h>
#include<assert.h>
{  
       assert(left);  
       assert(right);  
       if (right >=left)  
       {  
              T* mid =left+(right-left)/2;  
              if (*mid == data)  
                     return mid;  
              else  
                     return *mid > data ? BinarySearch(left, mid - 1, data) : BinarySearch(mid + 1, right, data);  
       }  
       else  
       {  
              return NULL;  
       }  
}  

分析画图等同非递归情况,假设最坏的情况下,循坏x次找到,有n/(2^x)=1,则x=log2 n

  递归的次数和深度都是log2 N,每次所需要的辅助空间都是常数级别的:

  时间复杂度:O(log2 N)

  空间复杂度:O(log2N )
3.斐波那契数列的时间复杂度及空间复杂度。
 
<1>非递归
int fib(int a,int b,int num)
{
    int c;

    if (num <= 0)
        return -1;
    else if (num == 1)
        return a;
    else if (num == 2)
        return b;
    else
    {
        while (num - 2)
        {
            c = a + b;
            a = b;
            b = c;
            num--;
        }
        return c;
    }

}

int main()
{
    int n;
    int result;

    printf("Input n\n");
    scanf("%d", &n);

    result = fib(2, 3, n);//可自定义输入第一个数和第二个数
    if (result == -1)
    {
        printf("Input Error!\n");
    }
    else
    {
        printf("n is %d\n", result);
    }

    return 0;
}

时间复杂度分析:

   从n(>2)开始计算,用F(n-1)和F(n-2)两个数相加求出结果,这样就避免了大量的重复计算,它的效率比递归算法快得多,算法的时间复杂度与n成正比,即算法的时间复杂度为O(n).

  时间复杂度O(n)
  空间复杂度O(1) 

<2>递归
int fib(int num)
{
    if (num < 0)
        return -1;
    if (num <= 2 && num > 0)
        return 1;
    else
        return fib(num - 1) + fib(num - 2);

}

int main()
{
    int n;
    int result;

    printf("Input n\n");
    scanf("%d", &n);

    result = fib(n);

    if (result == -1)
        printf("Input Error!\n");
    else
        printf("Result is %d\n", result);

    return 0;
}

   

 在递归调用过程中Fib(3)被计算了2次,Fib(2)被计算了3次。Fib(1)被调用了5次,Fib(0)中被调用了3次。所以,递归的效率低下,但优点是代码简单,容易理解。

 递归算法时间复杂度为(二叉树的节点个数):O()=(2^h)-1=2^n。空间复杂度为树的高度:h即o(n).

  时间复杂度O(2^N)

  空间复杂度O(N)  





猜你喜欢

转载自blog.csdn.net/yzyjaaa/article/details/80430653