- 写在前面:记录学习《恋上数据结构与算法》的过程。
- 课程链接地址:https://ke.qq.com/course/385223
学习大纲:
复杂度
如何评判一个算法的好坏?
package com.mj; public class Main { /* 0 1 2 3 4 5 * 0 1 1 2 3 5 8 13 .... */ public static int fib1(int n) { if (n <= 1) return n; return fib1(n - 1) + fib1(n - 2); } 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) { int n = 12; System.out.println(fib2(n)); System.out.println(fib3(n)); TimeTool.check("fib1", new Task() { public void execute() { System.out.println(fib1(n)); } }); TimeTool.check("fib2", new Task() { public void execute() { System.out.println(fib2(n)); } }); }
package com.mj; import java.text.SimpleDateFormat; import java.util.Date; public class Times { private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS"); public interface Task { void execute(); } public static void test(String title, Task task) { if (task == null) return; title = (title == null) ? "" : ("【" + title + "】"); System.out.println(title); System.out.println("开始:" + fmt.format(new Date())); long begin = System.currentTimeMillis(); task.execute(); long end = System.currentTimeMillis(); System.out.println("结束:" + fmt.format(new Date())); double delta = (end - begin) / 1000.0; System.out.println("耗时:" + delta + "秒"); System.out.println("-------------------------------------"); } }
- 如果单从执行效率上进行评估,可能会想到这么一种方案比较不同算法对同一组输入的执行处理时间
- 这种方案也叫做:事后统计法
- 上述方案有比较明显的缺点
- 执行时间严重依赖硬件以及运行时各种不确定的环境因素必须编写相应的测算代码
- 测试数据的选择比较难保证公正性
- 般从以下维度来评估算法的优劣
- 正确性、可读性、健壮性(对不合理输入的反应能力和处理能力)
- 时间复杂度(time complexity):估算程序指令的执行次数(执行时间)
- 空间复杂度(space complexity):估算所需占用的存储空间
大O表示法(Big O)
- 一般用大O表示法来描述复杂度,它表示的是数据规模 n 对应的复杂度
- 忽略常数、系数、低阶
- 注意:大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率
对数阶一般省略底数
- 所以 log2n 、log9n 统称为 logn
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); } } }
常见的复杂度
- 可以借助函数生成工具对比复杂度的大小
- 数据规模较小时
- 数据规模较大时
- fib函数的时间复杂度分析
public static int fib1(int n) { if (n <= 1) return n; return fib1(n - 1) + fib1(n - 2); }
- 所以复杂度是 O(2^n)
多个数据规模的情况
算法的优化方向
- 用尽量少的存储空间用
- 尽量少的执行步骤(执行时间)
- 根据情况,可以
- 空间换时间
- 时间换空间
Leetcode
- 一个用于练习算法的好网站