前言:
相信同学们在平时学习刷题中肯定会遇见这么一个名词:“时间复杂度”和“空间复杂度”。不少同学都是一头雾水,甚至有一点点了解,但是也说不出什么所以然来,那么今天咱们就从浅至深,来说说这个“时间复杂度”和“空间复杂度”究竟是个什么玩意儿!
一、为啥要有这么个东西?
在咱们计算机发展的早期,那时候的技术没咱现在高超,存储技术还是比较落后的,在编程的时候往往会因为一个不良的算法而导致项目的失败;所以那时候的人们,为了区别出一个算法和另一个算法的优劣好坏,便想到了一个方法来分析算法的效率,即时间复杂度和空间复杂度;时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间。
现在,经过多年的行业发展,我们的存储技术已经很高了,已经是不再特别关注一个算法的空间复杂度了。
二、时间复杂度
2.1、时间复杂度概念
咱们先来看看它的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。
可能这时候就会有同学来问啦:为什么来算时间复杂度的时候咱不能掐着表来看它跑一个程序所用的时间呢?哈哈哈,其实只有你自己的时候,你掐个表来记录每个算法所跑的时间,那也未尝不可,可是你得知道,每个人的设备性能都是不一样的,有好有坏,而且还受到各种条件约束和限制,所以咱们只能采取一种通用的分析方式来分析一个算法的时间复杂度。
一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
2.2、大O的渐进表示法
我们先来看看一个简单的栗子:
在上图这个函数中,只有一个循环语句,语句中的“ a++ ”一共执行了n次(for循环执行了n-0次a++),所以这个函数的基本操作:F(n) = n ;
然后我们再来举一个栗子:
这个函数中,第一个for循环里面嵌套了一个for循环,所以“ count++ ”这个语句一共执行了n^2(n的平方)次,第二个for循环中执行了2n次的“ count++ ”,最后一个while循环执行了10次的“ count++ ”,所以整个函数的基本操作:F(n) = n^2 + 2*n + 10;
那么你以为这就是该算法的时间复杂度了吗?其实不是的,实际上我们在计算时间复杂度时,不会一定要求计算出精准的执行次数,那么为什么呢?
由以上两个例子我们知道,执行函数中的n是未知数,咱们并不知道大小,有可能n只是一个很小的数据,也有可能n是一个很大很大的数据,当n足够大,达到100、1000,甚至一万时,F(n) = n^2 + 2*n + 10这个表达式的后两项就不能很好的左右整体结果的大小:
即我们推导大O阶表示法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
那么使用大O的渐进表示法后,func1得到的时间复杂度为:F(n^2)。
接下来咱们再举一个栗子讲讲在递归中,时间复杂度如何计算:
在上图这个递归计算N的阶乘函数factorial中,虽然没有循环语句,但是在每次递归调用自己时也会不断执行“ factorial(N-1) * N ”这个语句,一共执行了N次,所以这个递归函数的时间复杂度也为:O(N)。
三、空间复杂度
和时间复杂度类似,空间复杂度也不是来求一个算法具体所消耗的空间的,是对一个算法临时额外占用空间的一个量度,具体计算规则也和时间复杂度一样,采用大O渐进表示法。
就拿冒泡排序举例:
在冒泡排序中,只开辟了一次内存空间,即“ boolean sorted = true ”,后续循环中每次执行循环体都没有开辟内存,只在原来基础上进行操作,所以只是使用了常数个的额外空间,所以空间复杂度就为:O(1)。
那么递归函数的空间复杂度是咋样呢?这里我们同样拿阶乘来举例:
在递归中,每次调用递归函数都会返回并存储一份数据,直至最后一次递归函数的完成,所以这个递归函数的空间复杂度就为O(n)。
四、总结
时间复杂度和空间复杂度并不算很难的知识点,相信大家只要多多细心,多多刷题,一定可以完全掌握这个知识点,熟能生巧!
最后附上一张常见排序的时间空间复杂度的总结的表,希望可以帮助到大家:
排序方法 | 时间复杂度 | 空间复杂度 | ||
平均情况 | 最坏情况 | 最好情况 | ||
插入排序 | O(n^2) | O(n^2) | O(n) | O(1) |
希尔排序 | O(n^1.3) | O(n^2) | O(n) | O(1) |
冒泡排序 | O(n^2) | O(n^2) | O(n) | O(1) |
快速排序 | O(nlog2^n) | O(n^2) | O(nlog2^n) | O(log2^n) |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) |
堆排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(1) |
归并排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(n) |