1.算法复杂度
什么是算法?
算法是用于解决特定问题的一系列的执行步骤,使用不同算法,解决痛一个问题,效率可能相差非常大.
求第 n 个斐波那契数
package zh.algorithm; public class Fibonacci { // 方法1:递归,因为fib(n)必须要计算前面两个数,所以复杂度为2*2*2....=2n
public static int fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } // 方法2:循环,复杂度为O(n) public static int fib2(int n) { if (n <= 1) return n; int first = 0; int second = 1; for (int i = 0; i < n - 1; i++) { int sum = first + second; first = second; second = sum; } return second; } public static void main(String[] args) { // System.out.println(fib(0)); // System.out.println(fib(1)); // System.out.println(fib(2)); // System.out.println(fib(3)); // System.out.println(fib(4)); // System.out.println(fib(5)); // Output:0 1 1 2 3 5 System.out.println(fib2(64));// Output 1640636603 } }
如何评判一个算法的好坏
方法1:比较不同算法对同一组输入的执行处理时间(事后统计法),缺点
- 执行时间严重依赖硬件以及运行时各种不确定的环境因素
- 必须编写相应的测算时间代码
- 测试数据的选择难以保证公正性
方法2:从以下维度来评估算法的优劣:
- 正确性,可读性,健壮性(对不合理输入的反应能力和处理能力)
- 时间复杂度(time complexity):估算程序指令的执行次数(执行时间)
- 空间复杂度(space complexity):估算所需占用的存储空间
大O表示法
一般用大O表示法来描述复杂度,它表示的是数据规模n对应的复杂度,可以忽略常数,系数,低阶.
- 9 >> O(1)
- 2n+3 >> O(n)
- n2+2n+6 >> O(n2)
- 4n3+3n2+22n+100 >>O(n3)
- log2n,log3n,log8n >>logn
复杂度排序
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)
而通常时间复杂度与运行时间有一些常见的比例关系:
复杂度 | 10 | 20 | 50 | 100 | 1000 | 10000 | 100000 |
O(1) |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
O(log2(n)) |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
O(n) |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
O(n*log2(n)) |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
<1s |
O(n2) |
<1s |
<1s |
<1s |
<1s |
<1s |
2s |
3-4 min |
O(n3) |
<1s |
<1s |
<1s |
<1s |
20s |
5 hours |
231 days |
O(2n) |
<1s |
<1s |
260 days |
hangs |
hangs |
hangs |
hangs |
O(n!) |
<1s |
hangs |
hangs |
hangs |
hangs |
hangs |
hangs |
O(nn) |
3-4 min |
hangs |
hangs |
hangs |
hangs |
hangs |
hangs |
常见复杂度分析
public class Main { /* 0 1 2 3 4 5 * 0 1 1 2 3 5 8 13 .... */ // O(2^n) public static int fib1(int n) { if (n <= 1) return n; return fib1(n - 1) + fib1(n - 2); } // O(n) public static int fib2(int n) { if (n <= 1) return n; int first = 0; int second = 1; for (int i = 0; i < n - 1; i++) { int sum = first + second; first = second; second = sum; } return second; }public static void test1(int n) {// 1 if (n > 10) { System.out.println("n > 10"); } else if (n > 5) { // 2 System.out.println("n > 5"); } else { System.out.println("n <= 5"); } // 1 + 4 + 4 + 4 for (int i = 0; i < 4; i++) { System.out.println("test"); } // 140000 // O(1) // O(1) } public static void test2(int n) { // O(n) // 1 + 3n for (int i = 0; i < n; i++) { System.out.println("test"); } } public static void test3(int n) { // 1 + 2n + n * (1 + 3n) // 1 + 2n + n + 3n^2 // 3n^2 + 3n + 1 // O(n^2) // O(n) for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { System.out.println("test"); } } } public static void test4(int n) { // 1 + 2n + n * (1 + 45) // 1 + 2n + 46n // 48n + 1 // O(n) for (int i = 0; i < n; i++) { for (int j = 0; j < 15; j++) { System.out.println("test"); } } } public static void test5(int n) { // 8 = 2^3 // 16 = 2^4 // 3 = log2(8) // 4 = log2(16) // 执行次数 = log2(n) // O(logn) while ((n = n / 2) > 0) { System.out.println("test"); } } public static void test6(int n) { // log5(n) // O(logn) while ((n = n / 5) > 0) { System.out.println("test"); } } public static void test7(int n) { // 1 + 2*log2(n) + log2(n) * (1 + 3n) // 1 + 3*log2(n) + 2 * nlog2(n) // O(nlogn) for (int i = 1; i < n; i = i * 2) { // 1 + 3n for (int j = 0; j < n; j++) { System.out.println("test"); } } } public static void test10(int n) { // O(n) int a = 10; int b = 20; int c = a + b; int[] array = new int[n]; for (int i = 0; i < array.length; i++) { System.out.println(array[i] + c); } } }