时间复杂度
这段时间准备复习考研,发现时间复杂度一直没整明白,昨晚上听了节王道的网课感觉比之前更清晰一些了,所以把它写下来
频度:一个语句的频度是指该语句被重复执行的次数。
算法问题的规模:n
假设每个语句的执行一次的时间都是1ms,那么以下语句的时间开销是多少呢?
当n=3000时
- int i=1 执行了1次
- while(i<=n) 执行了3001次
- i++ 执行了3000次
- printf(“I Love You %d \n”,i); 执行了3000次
- printf(“I Love You More Than %d\n”,n); 执行了1次
执行的时间= (1+3001+3000+3000+1)*1ms
.
-
那么问题在于,用算法写成程序后,程序运行的时间来估量算法的优秀程度是否合理?
答案显然是不合理, 如果同一个算法用C语言实现,和用java实现,低级语言的执行效率会更高,用的时间会更短,而它们本身的算法并无差异。 所以用程序跑的时间来衡量算法时间复杂度是扯淡的 -
因此在计算时间复杂度的时候,把时间单位毫秒直接省略。只看每条语句执行的次数。每条语句执行的次数之和作为时间复杂度。
然后我们不难发现,上述执行时间(1+3001+3000+3000+1),可以用问题规模n代入
得到 :
算法时间复杂度= 3n+3
因此时间复杂度是关于n的函数记作T(n)
这时会出现两个问题:
- 如果代码有几千行需不需要一行一行数?
- 如果T(n)=n^3+ n^4 +nlog2n+…太过复杂的情况,可不可以忽略掉某些部分?
下面来进行两组算法的比较:
n=3000时,
1.T1(n)=3n+3 vs T2(n)=3n
9003 vs 9000
2. T1(n)=n^2+3n+1000 vs T2(n)=n^2
9010000 vs 9000000
不难发现,T1和T2的结果其实差别不大。好比两个舍友你俩都有九百万,他炒股赚了一万,他没有比你有钱多少。因此得出结论:时间复杂度表达式,可以只考虑阶数最高的部分,常数部分和阶数低的部分可以省略
例如(3n+3可以忽略3,n^2+3n+1000可以忽略3n和1000)
系数其实也是可以忽略的 例如3n 和 n ,当n=10,一个10,一个30。同样是生活中,拿三十块的小伙伴和拿十块的小伙伴,拿三十块的就不见得富裕多少。
因此可以如此简化:
T(n)=3n+3 可以看作是 n
T(n)=n^2+3n+1000 可以看作是 n^2
这种简化方法,称为大O表示法,即:
T1(n)=O(n)
T2(n)=O(n^2)
此时还有第二个问题,如果代码有几千行需不需要一行一行数?
根据上述的原则,常数是可以被忽略的,因此按顺序执行的代码是可以忽略不记的,只需要关注最深层次内的循环,其中的一条语句的执行次数即可。
关于最深层次循环,其实这里是存在有一个坑的,什么是最深层次的循环? 可以通过下面这个例子学习,并且引出时间复杂度的两条规则
解析步骤:
- 正常执行语句忽略不计
- 选择外层循环的一条执行语句,i++ 次数为n
- 内层循环的语句 printf(“I am Iron Man\n”) ,次数为n^2
结果:T(n)=O(n)+O(n^2)
大O表示法的加法准则,选择其中比较高阶的一项作为结果所以
T(n)=O(n^2)
因此可以得到结论,时间复杂度只需要关心最内层的循环,选择其中一条语句的执行次数即可。忽略常数和系数,只关注数量级。