记录小码哥的恋上数据结构与算法(第一季) - 复杂度
什么是算法
算法是用于解决特定问题的一系列的执行步骤
eg:解决两数相加的问题
// 计算a和b的和
public static int plue(int a, int b){
return a + b;
}
eg:解决 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
解决了什么问题 比如两数相加问题 、求和问题
能解决问题就是算法呢
解决同一个问题 方法非常多 效率可能相差非常大
如果单从执行效率上进行评估,可能会想到这么一种方案
事后统计法 写代码去测试 和硬件有关 和输入有关
上述方案有比较明显的缺点:
一般从以下维度来评估算法的优劣:
算法首先要保证 正确性、可读性、健壮性 对不合理输入的反应能力和处理能力
时间、空间优化
由于现在硬件发展的较好,一般情况下我们更侧重于时间复杂度。
时间换空间
时空
package cn.liuawen;
public class demo {
// 计算 1+2+3+...+n 的和
public static int sum1(int n) {
int result = 0;
for (int i = 1; i <= n; i++) {
result += i;
}
return result;
}
// 计算 1+2+3+...+n 的和
public static int sum2(int n) {
return (1 + n) * n / 2;
}
public static void main(String[] args) {
System.out.println("sum1(10):"+sum1(10));
System.out.println("sum2(10):"+sum2(10));
}
}
一般用大O表示法来描述复杂度 它表示的是数据规模n对应的复杂度
忽略常数、系数、低阶
注意:大O表示法仅仅是一种粗略的分析模型 是一种估算 能帮助我们短时间内了解一个算法的执行效率
对数阶一般省略底数
常数忽略掉 log2n/log29 = log9n
所以 O(log2n) 、O(log9n) 统称为 O(logn)
logn
可以借助函数生成工具对比复杂度的大小
https://zh.numberempire.com/graphingcalculator.php
一个用于练习算法的网站 力扣 leetcode
https://leetcode-cn.com/
https://leetcode.com/
目的是练习算法
我们来练习一个道斐波那契数列吧
https://leetcode-cn.com/problems/fibonacci-number/
https://leetcode-cn.com/problems/fibonacci-number/
实现
/* 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 int fib3(int n) {
if (n <= 1) return n;
int first = 0;
int second = 1;
while (n-- > 1) {
second += first;
first = second - first;
}
return second;
}
public static int fib4(int n) {
double c = Math.sqrt(5);
return (int) ((Math.pow((1 + c) / 2, n) - Math.pow((1 - c) / 2, n)) / c);
}
// 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;
}
/*
// 也可以使用while循环
while (n-- > 1) {
second += first;
first = second - first;
}
*/
return second;
}
速度变快了,内存消耗还是很多…
开辟新的数组空间,用空间换时间。
public static int fib3(int n){
if(n <= 1) return n;
int[] fib = new int[n+1];
fib[0] = 0;
fib[1] = 1;
for(int i = 2; i < fib.length; i++){
fib[i] = fib[i-1] + fib[i-2];
}
return fib[n];
}
有时候算法之间的差距,往往比硬件方面的差距还要大
算法之间的差距 比硬件差多了
写代码要考虑 算法
时间复杂度:O(n + k)
public static void test(int n, int k){
for(int i = 0; i < n; i++){
System.out.println("test");
}
for (int i = 0; i < k; i++){
System.out.println("test");
}
}
更多复杂度相关的知识,会在后续讲解数据结构、算法的过程中穿插
最好、最坏复杂度
均摊复杂度
复杂度震荡
平均复杂度
…