【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode

复杂度

文章目录

  • 复杂度
  • 什么是算法
  • 如何评判一个算法的好坏
  • 大O表示法 Big O
  • 对数阶的细节
  • 常见的复杂度
  • Leetcode
  • 斐波那契数列复杂度分析
    • 斐波那契数列-递归
    • 斐波那契数列-循环
    • fib函数的时间复杂度分析
  • 多个数据规模的情况
  • 算法的优化方向
  • 更多知识

记录小码哥的恋上数据结构与算法(第一季) - 复杂度

什么是算法

什么是算法

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

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

解决了什么问题 比如两数相加问题 、求和问题

能解决问题就是算法呢

解决同一个问题 方法非常多 效率可能相差非常大

如何评判一个算法的好坏

如果单从执行效率上进行评估,可能会想到这么一种方案

  • 比较不同算法对同一组输入的执行处理时间
  • 这种方案也叫做:事后统计法

事后统计法 写代码去测试 和硬件有关 和输入有关

上述方案有比较明显的缺点:

  • 执行时间严重依赖硬件以及运行时各种不确定的环境因素
  • 必须编写相应的测算代码
  • 测试数据的选择比较难保证公正性

一般从以下维度来评估算法的优劣:

  • 正确性、可读性、健壮性(对不合理输入的反应能力和处理能力)
  • 时间复杂度(time complexity) 估算程序指令的执行次数(执行时间)
  • 空间复杂度(space complexity)估算所需占用的存储空间

算法首先要保证 正确性、可读性、健壮性 对不合理输入的反应能力和处理能力

时间、空间优化

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

时间换空间

时空

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表示法 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 = og2n

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第1张图片

常数忽略掉 log2n/log29 = log9n

所以 O(log2n) 、O(log9n) 统称为 O(logn)

常见的复杂度

logn

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第2张图片

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

https://zh.numberempire.com/graphingcalculator.php

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第3张图片

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第4张图片

Leetcode

一个用于练习算法的网站 力扣 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);
}

复杂度分析:

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第5张图片

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第6张图片

呈现的是指数级增长的趋势

效率很低很低。

斐波那契数列-循环

不开辟任何空间,只使用循环完成。

// 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];
}

fib函数的时间复杂度分析

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第7张图片

有时候算法之间的差距,往往比硬件方面的差距还要大

【恋上数据结构与算法】复杂度、斐波那契数列、LeetCode_第8张图片

算法之间的差距 比硬件差多了

写代码要考虑 算法

多个数据规模的情况

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