评估算法优劣的核心指标是什么?
- 时间复杂度(流程决定)
- 额外空间复杂度(流程决定)
- 常数项时间(实现细节决定)
比拼算法最好的方式,不是分析复杂度,而是跑数据,实践为王。
复杂度
主要还是从算法所占用的「时间」和「空间」两个维度去考量。
- 时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述。
- 空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。
时间复杂度
时间复杂度的表达方式:「 大O符号表示法 」,即 T(n) = O(f(n))
其中f(n) 表示每行代码执行次数之和,而 O 表示正比例关系,这个公式的全称是:算法的渐进时间复杂度。
大O符号表示法并不是用于来真实代表算法的执行时间的,它是用来表示代码执行时间的增长变化趋势的。
-
常见的时间复杂度量级
常数阶O(1)
对数阶O(logN)
线性阶O(n)
线性对数阶O(n*logN)
平方阶O(n²)
立方阶O(n³)
K次方阶O(n^k)
指数阶O(2^n)
阶乘阶O(N!)
从上至下依次的时间复杂度越来越大,执行的效率越来越低。 -
时间复杂度怎么估算?步骤如下
(1)常数时间的操作
一个操作的执行时间不以具体样本量为转移,每次执行时间都是固定时间。称这样的操作为常数时间的操作。常见常数操作: * 常见的算术运算(+、-、*、/、% 等) * 常见的位运算(>>、>>>、<<、|、&、^等) * 赋值、比较、自增、自减操作等 * 数组寻址操作
(2)确定算法流程的总操作数量与样本数量之间的表达式关系
1. 想象该算法流程所处理的数据状况,要按照最差情况来 2. 把整个流程彻底拆分为一个个基本动作,保证每个动作都是常数时间的操作 3. 如果数据量为N,看看基本动作的数量和N是什么关系
(3)只看表达式最高阶项的部分
当完成了表达式的建立,只要把最高阶项留下即可。低阶项都去掉,高阶项的系数也去掉。记为:O(忽略掉系数的高阶项)总之一句话:将数据按照最差情况拆分成常数时间操作,假设数量级为N,表达式表示常数项操作和数量N之间的关系。取最高项系数,记为O(忽略掉系数的高阶项),即为时间复杂度
空间复杂度
空间复杂度也不是用来计算程序实际占用的空间的,空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势,我们用 S(n) 来定义。
空间复杂度比较常用的有:O(1)、O(n)、O(n²)。主要看变量分配的空间是否随着处理数据量变化。
对数器
当我们想验证一个算法正确与否时,一般需要自己寻找测试用例,而自己寻找的测试用例很难覆盖到全部情况,这时候就需要一种手段来帮我们验证算法是否正确,而对数器就是这样的手段。
常见使用方式:
- 你想要测的方法a
- 实现复杂度不好但是容易实现的方法b
- 实现一个随机样本产生器
- 把方法a和方法b跑相同的随机样本,看看得到的结果是否一样
- 如果有一个随机样本使得比对结果不一致,打印样本进行人工干预,改对方法a和方法b
- 当样本数量很多时比对测试依然正确,可以确定方法a已经正确。
二分法
对数据在一定规则情况下二分查找,需注意:无须保证数据有序,只要能正确构建左右两侧的淘汰逻辑,你就可以二分
异或运算
二进制计算方法:常见同或异或运算,二者互为非运算。
异或运算:相同为0,不同为1
同或运算:相同以1,不同为0
虽然口诀很简单,但是我猜各位还是记不住,这里针对异或运算,提出一种无进位相加计算方法。
比如:6^7 = 110 ^ 111 中第一位1+1=0 ,第二位1+1=0,第三位0+1=1 ,所以结果为001=2。
无进位相加,就是二进制想加,不进位计算,只本位计算。
- 异或运算的性质
(1)0^N == N , N^N == 0
(2)异或运算满足交换律和结合率 - 异或运算的常见应用
不用额外变量交换两个数:
甲= 甲^乙, (第一步:甲=甲 ^ 乙 ,乙=乙)
乙= 乙 ^甲, (第二步:甲=甲 ^ 乙 ,乙= 甲 ^ 乙 ^ 乙 =甲)
甲= 甲 ^乙。 (第三步:甲=甲 ^ 乙 ^甲 =乙 ,乙= 甲)
如此将甲乙的值对调。
更多精彩请关注微信公众号