文章目录
我们如何判断一个算法的好与坏,如何对比不同的算法呢。答案就是 时间复杂度 和1空间复杂的。
1.1 时间复杂度
1.1.1 大O表示法
大O表示法,也就是算的的渐进时间复杂度
T(n) = O(f(n))
当量级增加时,我们算法执行时间的一个增长的趋势。
此处的 f(n) 代表的就是算法执行的次数
O 则代表一个正比关系。
1.1.2 举例1
for (int i = 1; i <= n; i++){
x++;
}
以上For Loop,有 N 层循环。
首先分析代码中各部分的执行次数:
- 首先,int i = 1 是只执行一次的。
- 之后是判断 i <= n;
- 然后是 x++;
- 再是 i++;
- 返回2,继续执行,直到 i > n;
由此可见,一共执行的次数为 1 + 3n 次,所以有 O(1 + 3n) == O(N)。
最后为什么会被简化为 O(n)呢?原因是大O表示法计算的是执行次数n接近于无限大的比例。当n接近于无限大时,这个1(常数) 和 3 对整体的影响已经无所谓了。
1.1.3 举例2
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
x++
}
}
分析执行次数:
内循环:
- int j = 1 执行 1 次j
- 之后是判断 j <= n;
- 然后是 x++;
- 再是 j++;
- 返回2,继续执行,直到 i > n;
此时执行了 1 + 3n 次,在加上外层循环,则变为了 n + 3n2 次。在大O表示法中,我们取高阶,所以为 O( 3n2), 接着继续简化,则为 O(n2)。
1.1.4 举例3
for (int i = 1; i <= n; i++){
x++;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
x++
}
}
理解了前两个例子,那么这个例子也就简单了,将两个相加就是它的时间复杂度 O(n + n2),和上一个例子一样,取高阶,则为 O(n2)。
1.1.5 举例4
int i =1;
while(i<n){
i = i * 2;
}
这个就比较有意思,我们的 i 随着执行次数的增加而越来越接近n,此时,我们假设长执行 k 次以后就会等于n,那么则有
- 2k = n;
- k = logN;
这里不太好理解,需要多感受一下。当 i=1 时,i = i*2 循环k次的结果就可以化成2k。
此时时间复杂度则为 O(logN)。
1.1.6 举例4
for(int i = 0; i <= n; i++){
int i =1;
while(i<n){
i = i * 2;
}
}
内层我们刚才求过了,就是 logN,外层是我们也求过了,就是 n,那么则有 O(n*logN)。
1.2 复杂度指标
- O (Big O): 最差情况
- Ω (Big Omega): 最好情况
- θ (Big Theta): 一个算法的区间
1.3 空间复杂度
随着量级增加,内存空间增长的趋势
比较常见的呢,就是 O(1), O(n), O(n2)》
1.3.1 举例一
int x = 0;
int y = 0;
x++;
y++;
不管我们的x,y多大,都不会影响我们内存空间的分配,所以这是个 O(1)。
1.3.2 举例二
int[] arrArrey = new int[n]
for(int i = 0; i < n; i++){
newArry[i] = i;
}
此时n影响着我们的空间分配。n 越大,需要分配的空间也就越大,如果数组长度为 10,则需要 10 个空间。所以此时为O(n)。
1.3.3 举例三
如果是一个2 x 2的矩阵,此时则需要n*n个空间;来分配,所以此时为O(n2)。
1.4 总结
平时比较看重的还是时间复杂度吧。因为代码的运行次数是无法通过其他途径去改变的。
为什么空间复杂度就略显弱势呢,因为氪金大佬可以升级内存啊,内存足够大,我们还管他什么内存占多占少,没有意义呀!