在数学领域里,算法是用于解决某一类问题的公式和思想。
在计算机科学领域的算法,它的本质是一系列程序指令,用于解决特定的运算和逻辑问题。
当有一个需求,别人的代码运行起来只要100ms,只消耗5MB的内存,而自己的代码运行要100s,消耗500MB的内存,由此可见程序的好坏。
可是,如果代码都还没运行,如何预知代码运行所花的时间?由于受运行环境和输入规模的影响,代码的绝对执行时间是无法预估的。但我们可以预估代码的基本操作执行次数。
一、衡量算法好坏的重要标准有两个。
- 时间复杂度(大O表示法):执行算法的时间成本
- 空间复杂度(大O表示法):执行算法的空间成本
1、时间复杂度
直白的讲,时间复杂度就是把程序的相对执行函数T(n) 简化成一个数量级,这个数量级可以是n、n²、n³等。
如何推到出时间复杂度?有如下几个原则
- 如果运行时间是常数量级,则用常数1表示
- 只保留时间函数中的最高阶项
- 如果最高阶项存在,则省去最高阶项前面的系数
T(n) = 3n 最高阶项为3n,省去系数3,则转化的时间复杂度为T(n)=O(n)
T(n) = 3logn … 则转化的时间复杂度为T(n)=O(logn)
T(n) = 2 只有常数量级 则转化的时间复杂度为T(n)=O(1)
T(n) = 0.5n²+0.5n 最高阶项为0.5n²,省去系数0.5,T(n)=O(n²)
比较谁的执行用时更长?
当n取值足够大,O(1)<O(logn)<O(n)<O(nlogn)<O(n²)
2、空间复杂度
常见的空间复杂度有下面几种情形
- 常量空间
当算法的存储空间大小固定,和输入规模没有直接的关系时,空间复杂度记做O(1).
const fun1 = (n:number) => {
const a:number = 3
}
- 线性空间
当算法分配的空间是一个线性的集合(如数组),并且集合大小和输入规模n成正比,空间复杂度记做O(n)
const fun2 = (n:string) => {
const arr:string[] = [n]
}
- 二维空间
当算法分配的空间是一个二维数组,并且集合的长度和宽度都与输入规模n成正比,空间复杂度记做O(n²) - 递归空间
递归是一个比较特殊的场景,虽然递归代码中并没有显式的声明变量或集合,但是计算机在执行程序时,会专门分配一块内存,用来存储“方法调用栈”。纯粹的递归操作的空间复杂度也是线性的,如果递归的深度是n,name空间复杂度就是O(n)
3、时间与空间的取舍
因为计算机的运算速度和空间资源是有限的。
很多时候需要在时间复杂度和空间复杂度之间进行取舍。
比如双重for循环的时间复杂度是O(n²),空间复杂度是O(1).
二、数据结构
1、数组
2、链表
3、栈和队列
4、散列表
5、树和二叉树
6、二叉堆
(…还没有整理完,后续补充…)