1.什么是算法?
算法是一种解决某类问题、具体的、明确无歧义的计算过程。
2.数量级
十进制的指数。
例如: 1500 = 1.5*(10**3)数量级是3,也可以是 “千”,kilo
150万 = 1.5*(10**6) 数量级是6, 也可以是“百万”, ‘million’
150万比1500大3个数量级
3.输入规模
执行环境资源有限,需要根据输入规模(数量级)准备资源,比预估的高一个数量级就可以。
例如: React的VirtualDOM 应该支持[万]数量级数据(默认页面最大节点数在“千”级),在“毫秒”时间内完成一次计算。
因为:显示器更新时间时间间隔10ms,算法应该<这个数量级,为了显示器显示不卡
4. 算法依赖的数学模型
CPU包含:
短期记忆(寄存器-Register)
推理计算(算法逻辑单元-ALU-Arithmetic and Logic Unit)
系统将数据从内存读取到寄存器,计算完成后再存回内存
内存:
长期记忆(随机存储器-RAM-数据空间地址+指令空间地址)
访问每个地址时间相同
例如:5000*0.2=1000; 数据计算时都要在寄存器中,结果返回也返回到寄存器中
5. 算法的时间复杂度和空间复杂度
时间复杂度和空间复杂度都是对算法的一个分类
时间复杂度
在该算法中执行次数最多的代码行,执行的次数,表示时间复杂度。因为每个命令,代表固定的指令和CPU执行周期,代表固定的时间。
时间复杂度分类
示例
长度为n的一维数组遍历-----O(n) n*n的二维数组嵌套遍历 --------O(n^2) n*n*n的三维数组嵌套遍历------O(n^3) n*m的二维数组嵌套遍历-----------O(n*m) 长度n的数组和m的数组分别遍历----O(n+m) 运算次数为常数---------------O(1)
空间复杂度
空间复杂度是指算法用了多少额外的空间,可以理解成新创建了多少空间。没有额外创建的话O(1)
空间复杂度分类的表示方法和时间复杂度雷同。
例如:
// 实现长度为n的数组的反转 function f(A) { const reverseA = []; for(let i=0; i<A.length; i++) { reverseA.push(A.pop(A)) } return reverseA; // 相当于创建了一个长度为n的空间,复杂度O(n) }
上面的方法如果想要实现空间复杂度O(1)。则只能在原来数组
// 实现长度为n的数组的反转 function f(A) { let l = 0, r=A.length-1; while(l<r) { let temp = A[l]; A[l++] = A[r]; A[r--] = temp; } }
⚠️对于递归算法,函数参数所占用的空间会累计。
6.复杂度的本质
复杂度是一种度量指标(消耗时间和空间)随着输入规模增长而增常的一种关系。根据不同的关系有不同的分类。
O(1)+O(1)=O(1); O(n)+O(n)=O(n); O(lgn)+O(lgn)=O(lgn); O(lgn)+O(n)=O(n); O(n)+O(1)=O(n); O(n^2)+O(n)=O(n^2);
由上面可知,复杂度是以最大消耗作为计算。
O其实是复杂度的渐近上界(最坏的情况)。
Ω是复杂度渐近下界(最好);?是渐近紧密界(中间)。
复杂度分类: O(1),O(lgn), O(√n),O(n),O(nlgn),O(n^2),O(2^n),O(n!)
绿色部分是单机一般能解决;黄色需要用集群等;红色部分属于宕机算法。
降低复杂度的方法:
// 分治策略 O(n^2) -> O(nlgn) // 散列 O(n) -> O(1) O(n) -> O(k) // 二叉树 O(n) -> O(lgn) // 链表 O(n) -> O(1) // 动态规划 O(2^n) -> O(n^2) O(n!) -> O(n^2)