复杂度与LeetCode

复杂度

什么是算法?

算法是用于解决特定问题的一系列的执行步骤

// 计算a和b的和
public static int plus(int a, int b){
	return a + b;
}
// 计算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):估算所需占用的存储空间

// 计算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;
}

由于现在硬件发展的较好,一般情况下我们更侧重于时间复杂度

大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表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率

对数阶的细节

对数阶一般省略底数

  • log2n = log29 ∗ log2n

所以 log2n 、log9n 统称为 logn

常见的复杂度

O(1) < O(logn) < O(n) < O(nlogn) < O(n2 ) < O(n3 ) < O(2n ) < O(n!) < O(nn)

可以借助函数生成工具对比复杂度的大小

函数图像绘制工具

数据规模较小时

数据规模较大时

多个数据规模的情况

时间复杂度: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");
	}
}

LeetCode刷题指南

首先去 https://leetcode-cn.com/ 注册一个力扣账号。

我们以力扣上一道斐波那契的题目为例,顺便分析算法的时间复杂度。
题目网址:LeetCode: 509.斐波那契数

斐波那契数列复杂度分析

斐波那契数列-递归

时间复杂度为:O(2n)

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;
	// 也可以使用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];
}

在力扣上执行上面代码发现没太大区别!

fib函数的时间复杂度分析

fib1 函数和fib2 函数的差别有多大?

如果有一台 1GHz 的普通计算机,运算速度 109 次每秒,n为64
O(n) 大约耗时 6.4 * 10-8
O(2n) 大约耗时 584.94
有时候算法之间的差距,往往比硬件方面的差距还要大

斐波那契的线性代数解法 – 特征方程

时间复杂度:视为 O(1)

算法的优化方向

用尽量少的存储空间

用尽量少的执行步骤(执行时间)

根据情况,可以

  • 空间换时间
  • 时间换空间
原文地址:https://www.cnblogs.com/netu/p/14860373.html