1.时间复杂度
1.1 什么是时间复杂度
时间复杂度是指执行算法所需要的计算工作量。也就是说,执行一段代码时,每行代码要执行次数的总和。
1.2 大O表示法
大O表示法其实就是对时间复杂度的一个粗略估算,可以让开发者快速了解一段代码的执行效率。毕竟如果把每一段代码的时间复杂度都计算出来有点麻烦,另外对于常数来说影响不大。因此,在引入大O表示法时,就是把常数忽略。
1.3 时间复杂度计算的案例
- 案例1
for(int i = 0; i < n; i++ ) {
for (int j = 0; j < n; j++) {
NSLog(@"test");
}
}
如何计算上面那段代码的时间复杂度呢?
1.int i = 0 , 执行了1次
2.i < n, 执行了n次
3.i++, 执行了n次
4. for (int j = 0; j < n; j++) { NSLog(@“test”); } , 要执行n次
4.1 int j = 0, 执行了1次
4.2 j < n, 执行了n次
4.3 j++ , 执行了n次
4.4. NSLog(@“test”); , 执行了n次
最后算出来的次数是 1 + n + n + n * (1 + n + n + n) = 1 + 3n + 3n^2;
用大O表示表达法就是O(n^2)
- 案例2
for(int i = 0; i < n; i += i ) {
for (int j = 0; j < n; j++) {
NSLog(@"test");
}
}
如何计算上面那段代码的时间复杂度呢?
1.int i = 0 , 执行了1次
2.i+= i, 执行了log2(n)次
3.i < n, 执行了log2(n)次
4. for (int j = 0; j < n; j++) { NSLog(@“test”); } , 执行了log2(n)次
4.1 int j = 0, 执行了1次
4.2 j < n, 执行了n次
4.3 j++ , 执行了n次
4.4. NSLog(@“test”); , 执行了n次
最后算出来的次数是 1 + log2(n) + log2(n) + log2(n) * (1 + n + n + n) = 1 + 3log2(n) + 3nlog2(n);
用大O表示表达法就是O(nlogn)
1.4 常见的时间复杂度
执行次数函数举例 | 阶 | 非正式术语 | 解释 |
---|---|---|---|
12 | O(1) | 常数阶 | 大部分程序的大部分指令之执行一次,或者最多几次。如果一个程序的所有指令都具有这样的性质,我们说这个程序的执行时间是常数。 |
2n+3 | O(n) | 线性阶 | 如果程序的运行时间的线性的,很可能是这样的情况:对每个输入的元素都做了少量的处理。当N=1 000 000时,运行时间大概也就是这个数值;当N增长到原来的两倍时,运行时间大概也增长到原来的两倍。如果一个算法必须处理N个输入(或者产生N个输出), 那么这种情况是最优的。 |
3n2+2n+1 | O(n2) | 平方阶 | 如果一个算法的运行时间是二次的(quadratic),那么它一般只能用于一些规模较小的问题。这样的运行时间通常存在于需要处理每一对输入 数据项的算法(在程序中很可能表现为一个嵌套循环)中,当N=1000时,运行时间是1 000 000;如果N增长到原来的两倍,则运行时间将增长到原来的四倍。 |
5log2n+20 | O(logn) | 对数阶 | 如果一个程序的运行时间是对数级的,则随着N的增大程序会渐渐慢下来,如果一个程序将一个大的问题分解成一系列更小的问题,每一步都将问题的规 模缩减成几分之一 ,一般就会出现这样的运行时间函数。在我们所关心的范围内,可以认为运行时间小于一个大的常数。对数的基数会影响这个常数,但改变不会太 大:当N=1000时,如果基数是10,logN等于3;如果基数是2,logN约等于10.当N=1 00 000,logN只是前值的两倍。当N时原来的两倍,logN只增长了一个常数因子:仅当从N增长到N平方时,logN才会增长到原来的两倍。 |
2n+3nlog2n+19 | O(nlogn) | nlogn阶 | 如果某个算法将问题分解成更小的子问题,独立地解决各个子问题,最后将结果综合起来 (如归并排序,堆排序),运行时间一般就是NlogN。我们找不到一个更好的形容, 就暂且将这样的算法运行时间叫做NlogN。当N=1 000 000时,NlogN大约是20 000 000。当N增长到原来的两倍,运行时间超过原来的两倍,但超过不是太多。 |
6n^3 + 2n^2+3n+4 | O(n3) | 立方阶 | 类似的,如果一个算法需要处理输入数据想的三元组(很可能表现为三重嵌套循环),其运行时间一般就是三次的,只能用于一些规模较小的问题。当N=100时,运行时间就是1 000 000;如果N增长到原来的两倍,运行时间将会增长到原来的八倍。 |
2^n | O(2^n)) | 指数阶 | 如果一个算法的运行时间是指数级的(exponential),一般它很难在实践中使用,即使这样的算法通常是对问题的直接求解。当N=20时,运行时间是1 000 000;如果增长到原来的两倍时,运行时间将是原时间的平方! |
各个时间负责度的对比如下图:
所消耗的时间从小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
2.斐波那契数
题目:
一组数如下:
0,1,1,2,3,5,8,13…
求第n个数的值
结题思路:
通过观察,每个数,都是前两个数的和,则得出,f(n) = f(n-1) + f(n-2)
代码实现:
- 实现方式一:递归算法
NSInteger jisuan1(int n) {
NSInteger result = 0;
if (n == 0) {
result = 0;
}
if (n == 1) {
result = 1;
};
if (n > 1) {
result = jisuan1(n-1) + jisuan1(n - 2);
}
return result;
}
但是这种算法,效率特别低,但n等于64时,几乎就卡死了
该算法的时间复杂度如下:O(2^n)
- 代码实现2
NSInteger jisuan(int n) {
NSInteger first = 0;
NSInteger second = 1;
for (int i = 2; i < n; i++) {
long tem = first + second;
first = second;
second = tem;
NSLog(@"%ld", second);
}
return second;
}
这种方式会快速很多。时间复杂度如下:O(n)