1. 复杂度

1 算法
  1. 算法是用于解决特定问题的一系列的执行步骤
  2. 使用不同算法,解决同一个问题,效率可能相差非常大
2 评判算法好坏
2.1 事后统计法
  1. 比较不同算法对同一组输入的执行处理时间
  2. 执行时间严重依赖硬件以及运行时各种不确定的环境因素
  3. 必须编写相应的测算代码
  4. 测试数据的选择比较难保证公正性
    1. 例如算法1处理100快,但处理50慢,而算法2处理100慢,处理50快
2.2 评估算法优劣的几个维度
  1. 正确性、可读性、健壮性(对不合理输入的反应能力和处理能力)
  2. 时间复杂度:
    1. 估算程序指令的执行次数(从而推断执行时间)
    2. 此处我们认为一行代码就执行一次,不考虑一行代码可能转为多条汇编指令的情况
    3. 一个方法中包含n条指令,那么调用方法时,执行次数算为n
  3. 空间复杂度:估算所需占用的存储空间
2.3 大O表示法(Big O)
  1. 一般用大O表示法来描述复杂度,它表示的是数据规模 n 对应的复杂度
  2. 忽略常数、系数、低阶
  3. 因此大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率
  4. 对数一般省略底数
//因为有如下公式,所以log2(n)、log9(n) 统称为logn
log9(n) = log2(9)*log9(2)
  1. 常见的复杂度
执行次数复杂度
12O(1)
2n + 3O(n)
4n^2 + 2n + 6O(n^2)
4log2(n) + 25O(logn)
3n + 2nlog3(n) + 15O(nlogn)
4n^3 + 3n^2 + 22n + 100O(n^3)
2^nO(2^n)
  1. 常见复杂度排序
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
  1. 多个数据规模的情况
//时间复杂度:O(n+k)
public static void test11(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");
	}
}
3 斐波那契数相关的算法问题
  1. 斐波那契数:0 1 1 2 3 5 8 13 21 34 …
  2. 代码
package com.mj;

public class Main {
	// 算法一
	// 时间复杂度: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;
	}
	//算法三
	//时间复杂度:O(n)
	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;
	}
	
	//算法四:特征方程解法
	//时间复杂度:O(1)
	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);
	}
	public static void main(String[] args) {
		int n = 12;

		System.out.println(fib2(n));
		System.out.println(fib3(n));
		System.out.println(fib4(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));
//			}
//		});
	}

	public static void test1(int n) {
		// 汇编指令

		// 1(syso),判断忽略
		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(i=0) + 4(i++) + 4(<4) + 4(syso)
		for (int i = 0; i < 4; i++) {
			System.out.println("test");
		}

		// 140000
		// O(1)
		// 只用了一个变量i,所以认为空间复杂度为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;
		// 前面a、b、c大小都是常数,跟n无关
		// 但数组分配的空间大小,和n成正比,所以空间复杂度为O(n)
		int[] array = new int[n];
		for (int i = 0; i < array.length; i++) {
			System.out.println(array[i] + c);
		}
	}
}
  1. 工具类:Times
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. O(2^n)与O(n)两种方案间的差距(如果n为64)
    1. 如果有一台1GHz的普通计算机,运算速度为10^9次每秒
    2. O(n)大约耗时6.4 ∗ 10^−8秒
    3. O(2^n) 大约耗时584.94年
    4. 有时候算法之间的差距,往往比硬件方面的差距还要大
4 算法的优化方向
  1. 用尽量少的存储空间
  2. 用尽量少的执行步骤(执行时间)
  3. 根据情况
    1. 空间换时间
    2. 时间换空间
5 网站推荐
//leetcode
https://leetcode-cn.com/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值