1、函数的渐近增长
输入规模 n 在没有限制的情况下,只要超过一个数值 N (比如N=2 使得n > 2),这个函数就总是大于另一个函数。函数是渐近增长的。比如两个函数f(n)、g(n) 如果存在一个整数N ,对于所有的 n > N, 最终
f(n) > g(n),也就是 f(n) 增长渐近快于 g(n)。
// 函数的渐近增长
// 2n+3 算法A
int index = 0;
for (int i = 0; i < n; i++) {
index++;
}
for (int i = 0; i < n; i++) {
index++;
}
System.out.println(index);
System.out.println(index);
System.out.println(index);
// 3n+1 算法B
for (int i = 0; i < n; i++) {
index++;
}
for (int i = 0; i < n; i++) {
index++;
}
for (int i = 0; i < n; i++) {
index++;
}
System.out.println(index);
次数 | 2n+3 | 2n | 3n+1 | 3n |
---|---|---|---|---|
n=1 | 5 | 2 | 4 | 3 |
n=2 | 7 | 4 | 7 | 6 |
n=3 | 9 | 6 | 10 | 9 |
n=10 | 23 | 20 | 31 | 30 |
n=100 | 203 | 200 | 301 | 300 |
当输入规模n=1时,算法A 不如 算法B,当n > 2时算法A 优先于算法B,随着 n 的增加 算法A 比 算法B 要优秀(n越大 时间效率上的差异也就越来越大)。
随着 n 的增大,发现最高次项相乘的常数、加法常数、其它次要项是不影响最终算法变化的,所以可以进行忽略。
比如:2n+3、3n+1 常数3和1可以忽略 比较2n、3n即可。(忽略的只是加法常数)
比如 4n+8、2n^2 +1 常数8、1,相乘项4、2可以省略 比较n、n^2即可(略的是最高次项相乘的常数和加法常数)
比如 2n^2 + 3n + 1、2n^3 + 3n + 1,常数1、2,次要项3n都可以省略,只比较n^2 、n^3主项(最高阶项)的阶数即可。(忽略的是最高次项相乘的常数和其它次要项)
结论:判断一个算法的效率时,函数中的常数、最高次项相乘的常数和其它次要项常常可以忽略,更应该关注主要项(最高阶项)的阶数。某个算法随着 n 的增大,会越来越优于另一个算法,或者越来越差于另一个算法。
2、算法时间复杂度
随着规模 n 的增大,算法执行时间的增长率和f(n)的增长率相同。使用O()来表示 即:T(n) = O(f(n))。
一般情况下,随着 n 的增大,T(n)增长最慢的算法为最优算法。还记不记得前面讲的两个求和算法? 他们的时间复杂度分别是:O(n)、O(1)
T(n): 执行次数
n: 问题规模
f(n): 问题规模 n 的某个函数 这个函数可以理解为语句执行次数
1、推导大O阶方法
a、用常数"1"取代运行时间中的所有加法常数。
b、在修改后的运行次数函数中,只保留最高阶项。
c、如果最高阶项存在且不是1,则去除这个项相乘的常数。
2、时间复杂度术语
1、常数阶: O(1)
比如 : 还是前面等差数列求和例子,f(n)=2n+3、f(n)=3
第一步替换加法常数为1——> f(n)=2n+1 f(n)=1,
第二步保留最高项——>f(n)=2n f(n)=1,
第三步最高项不是1去除相乘常数——>f(n)=n f(n)=1。
最终根据T(n) =O(f(n)),两者时间复杂度就出来了O(n)、O(1)
2、线性阶:O(n)
比如 : 等差数列求和那个例子 f(n)=2n+3——>f(n)=n——>O(n)
3、对数阶: O(logn)
log对数是对求幂的逆运算.
2 ^ x = n, x=log2(N),相当于2的多少次方(立方)等于N,比如log2(8)=x,那么就是2^x =8,x=3
int count = 1;
while(count < n){
count = count * 2; 多少个 2 相乘后大于 n。
}
f(n)=logn——>O(logn)
4、平方阶: O(n^2)
比如嵌套循环:
int index = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
index++;
}
}
内部循环是O(n),对于外部循环来说再循环n次,得出结论O(n^2),若是外部 n 改为m,那么就是O(m*n)
上面已经列举出了常用的时间复杂度术语,耗费时间从小打大: O(1) < O(logn) < O(n) < O(n^2)
3、算法空间复杂度
常听到空间换时间、时间换空间。这里的空间在代码中是指存储空间,代码中完全可以通过硬盘或内存的存储来进行换取时间上的高效率。
即 S(n) = O(f(n)) n = 问题规模 f(n) = 关于 n 所占存储空间的函数。
时间复杂度指的是:运行时间的需求。
空间复杂度指的是:空间需求。
4、总结
- 这几章主要讲了数据结构和算法的一些基本概念,比如数据结构的定义、数据的逻辑结构和物理结构、算法的定义、算法的特性、时间空间复杂度等等。没有最好的算法,只有最适合的。
- 相信大家已经基本了解了数据结构和算法的相关概念。接下来,会继续讲解常见的数据结构