补充
- 数据结构和算法解决的是“如何让计算机更快时间、更省空间的解决问题”。
- 需从执行时间和占用空间两个维度来评估数据结构和算法的性能
- 分别用时间复杂度和空间复杂度两个概念来描述性能问题,二者统称为复杂度
- 复杂度描述的是算法执行时间(或占用空间)与数据规模的增长关系
一、为什么要进行复杂度分析(事后统计法)?
测试结果非常依赖测试环境、测试结果受数据规模的影响很大
二、大O表示法
-
由来
我们通过对一段代码的总执行时间的计算,我们可以得到一个非常重要的规律:所有代码的执行时间T(n)与每行代码的执行次数n成正比。
T(n)表示代码执行的时间;n表示数据规模的大小;f(n)表示每行代码执行的次数总和。因为这是一个公式,所以用f(n)来表示。公式中的O,表示代码的执行时间T(n)与f(n)表达式成正比 -
表示法
大O时间复杂度表示法实际上并不具体表示代码真正执行时间,而是表示代码执行时间随数据规模增长的变化趋势(低阶、常量、系数这三部分并部左右增长趋势)。
三、时间复杂度分析
- 如何分析一段代码的时间复杂度?
- 只关注循环执行次数最多的一段代码
- 加法法则:总复杂度等于量级最大的那段代码的复杂度
- 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
四、几种常见时间复杂度实例分析
我们可以粗略地分为两类,多项式量级和非多项式量级。其中,非多项式量级只有两个:O(2^n)和O(n!)。
- O(1)
只要代码的执行时间不随n的增大而增大,这样代码的时间复杂度我们记作O(1)。或者说,一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1) 。 - O(logn)、O(nlogn)
i=1;
while (i <= n) {
i = i * 2;
}
- O(m+n)、O(m*n)
int cal(int m, int n) {
int sum_1 = 0;
int i = 1;
for (; i < m; ++i) {
sum_1 = sum_1 + i;
}
int sum_2 = 0;
int j = 1;
for (; j < n; ++j) {
sum_2 = sum_2 + j;
}
return sum_1 + s;
}
从代码中可以看出,m和n是表示两个数据规模。我们无法事先评估m和n谁的量级大,所以我们在表示复杂度的时候,就不能简单地利用加法法则,省略掉其中一个。所以,上面代码的时间复杂度就是O(m+n)。
五、如何掌握复杂度
关键在于多练,所谓熟能生巧