数据结构——时间复杂度

前言

当你拿到一段代码时,你该如何判断这一段代码算法的好坏程度?

有的人会说跑一下(运行一下),事后统计运行时间。当然这样确实能够直观的通过看运行程序所花费时间,但是这存在着一些问题:

  • 和机器性能有关
    • 超级计算机 vs 单片机(同样的一段代码一定是超级计算机运行的时间更快)
  • 和编程语言有关
    • 越高级的语言运行的效率越低
  • 编译程序产生的机器指令质量有关
  • 有些算法不能事后统计
    • 导弹控制算法(不能为了统计算法的效率发射一颗导弹试试)

所以综上,如果使用事后统计(先运行一下)的方法,算法运行的速度除了和自身算法的优劣有关,还和许多的外界因素有关。并且从上我们可以提出两个问题:

  • 能否排除外界与算法本身无关的因素?
  • 能否事先估计?

至此我们引入了一个概念:算法复杂度

算法时间复杂度

含义

事前预估算法时间开销T(n)与问题规模n的关系(T代表时间“Time”)

爱你三千遍

我们使用一段简单的代码来解释时间复杂度

#include

void loveU(int n){
    // n为问题规模
    int  i = 1;
    while(i<=n){
   
        printf("I love you %d\n times",i);
        i++;
    }
    printf("I love you more than %d\n",n);
}

int main(){
   
    loveU(3000);
}

代码分析

在主函数中,我们调用loveU()函数,并且传入问题规模n(3000)

我们看loveU()函数中的5条语句频度

int i = 1 // 执行1次
while(i<=n){ // 执行3001次
	printf("I love you %d\n",n); // 执行3000次
	i++; // 执行3000次
}
printf("I love you more than %d\n",n); // 执行1次

故: T ( 3000 ) = 1 + 3001 + 3000 ∗ 2 + 1 T(3000) = 1+ 3001 + 3000*2 + 1 T(3000)=1+3001+30002+1,得出时间开销与问题规模n的关系: T ( n ) = 3 n + 3 T(n) = 3n +3 T(n)=3n+3

推导过程:

1 + 3001 + 3000*2 + 1

-> (3001->3000+1) 1 + 1 + 1 + 3000*3

问题1:是否可以忽略表达式某些部分?

上述的例子中,问题规模n与时间开销的关系还算比较简单。

但是如果有一个表达式是: T ( n ) = n 3 + 6 n 2 + n + 1500 T(n)=n^3+6n^2+n+1500 T(n)=n3+6n2+n+1500 那么我们就很难一眼看出其复杂度。那么我们就会提出一个问题,是否可以忽略表达式某些部分呢?

回答1:可以忽略表达式某些部分

答案是肯定的,我们可以忽略表达式的某些部分。

忽略低阶保留最高阶(数量级)

要回答这个问题,我们可以通过上述的例子来理解:

n = 3000 n=3000 n=3000时:

3 n = 9000 3n=9000 3n=9000 vs 3 n + 3 = 9003 3n+3=9003 3n+3=9003

n 2 = 9000000 n^2=9000000 n2=9000000 vs n 2 + 3 n + 1000 = 9010 , 000 n^2+3n+1000=9010,000 n2+3n+1000=9010,000

n 3 = 27000000000 n^3=27000000000 n3=27000000000 vs n 3 + n 2 + 9999999 = 270189999999 n^3+n^2+9999999=270189999999

你可能感兴趣的:(数据结构,计算机408考研,数据结构)