1.复杂度
1.1什么是算法?
算法是用于解决特定问题的一系列的执行步骤。
以下算法是为了解决两数相加的问题:
// 计算a和b的和 public static int plue(int a, int b){ return a + b; 以下算法是为了解决 n个数字的和 的问题:
// 1+2+3+...+n public static int sum(int n){ int result = 0; for(int i = 1; i <= n; i++){ result += i; } return result; }
使用不同算法,解决同一个问题,效率可能相差非常大。比如:
求第 n 个斐波那契数(fibonacci number) 如何评判一个算法的好坏? 如果单从执行效率上进行评估,可能会想到这么一种方案:
比较不同算法对同一组输入的执行处理时间 这种方案也叫做:事后统计法 上述方案有比较明显的缺点:
执行时间严重依赖硬件以及运行时各种不确定的环境因素 必须编写相应的测算代码 测试数据的选择比较难保证公正性 一般从以下维度来评估算法的优劣:
正确性、可读性、健壮性(对不合理输入的反应能力和处理能力) 时间复杂度(time complexity):估算程序指令的执行次数(执行时间) 空间复杂度(space complexity):估算所需占用的存储空间 由于现在硬件发展的较好,一般情况下我们更侧重于时间复杂度。
下面代码是一个测试时间效率的小工具:
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("-------------------------------------"); } }
1.2大O表示法(Big O)
一般用大O表示法来描述复杂度,它表示的是数据规模 n 对应的复杂度。
忽略常数、系数、低阶:
9 >> O(1) 2n + 3 >> O(n) n2 + 2n + 6 >> O(n2) 4n3 + 3n2 + 22n + 100 >> O(n3) 写法上,n3 等价于 n^3 注意:大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率。
对数阶的细节 对数阶一般省略底数:
由于 log29 ∗ log9n >> log2n,即每个对数基本都可以常数乘另一个对数 所以 O(log2n) 、O(log9n) 统称为 O(logn)
1.3fib函数的时间复杂度分析
斐波那契数
public class Fib { //斐波那契数 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) { final int n = 45; Times.test("fib1", new Times.Task() { public void execute() { System.out.println(fib1(n)); } }); Times.test("fib2", new Times.Task() { public void execute() { System.out.println(fib2(n)); } }); } }
上面两种 fib 函数的差别有多大?
fib1函数采用递归的方式实现,fib会重复调用子递归,参考树形结构,复杂度为2^n次方
fib2采用循环的方式实现,复杂度仅为O(n)
如果有一台 1GHz 的普通计算机,运算速度 109 次每秒,n 为 64 O(n) 大约耗时 6.4 ∗ *∗ 10-8 秒 O(2n) 大约耗时 584.94 年 有时候算法之间的差距,往往比硬件方面的差距还要大
1.4算法的优化方向
-
用尽量少的存储空间
-
用尽量少的执行步骤(执行时间)
-
根据情况:空间换时间、时间换空间
1.5Leetcode
练习算法网站
平时练习可以不在leetcode网站上写,将模板复制下来到编译器上写
如果有的时候提交了效率不太行,点击详情可以查看效率高的人怎么写的