快速幂
求a的b次方(b为整型):
不妨记b的二进制为 bnbn−1...b0,其中 bi=0 or 1,则:
可以看到新的底数 a2i+1=a2ia2i,根据这个规律可以写出快速幂的代码(仅考虑 b>0 的情况):
public double Power(double a, int b) {
double base = a;
double result = 1;
while(b != 0){
//判断b二进制末位是1,即bi是1的情况下,result需要乘上新的底数
if((b & 1) == 1) result * = base;
//按照规律更新底数
base * = base;
//将b的二进制右移1位,即删掉原始末位
b >> = 1;
}
return result;
}
相比于循环 b 次做乘法,快速幂的时间复杂度仅为
字典序全排列
以元素互异的字符集{p1,p2,…,pn}为例,输出它的全排列:
假设字符元素之间存在某种偏序关系,例如整数集可以利用数值大小进行排序。全排列可以看成由字符集所有元素组成的字符串,如何不重复有序地输出该字符集的所有全排列,关键在于如何根据给定的全排列有规则地输出下一个全排列。
以{1,2,3}为例,其全排序为123,132,213,231,312,321,可以看到这里全排列的枚举方式保证了全排列的递增趋势,我们可以将这个递增的规则抽象出全排列生成的规则,从而避免重复枚举,或者遗漏。
根据上述例子,可以简述一下字典序全排列的步骤:
- 记当前的全排列p=p1...pn
- 找到j=max{i|pi<pi+1},k=max{i|pi>pj}
- 交换换pj,pk
- 将pj+1...pn这部分后缀进行翻转
可以证明一下这样得到的全排列,是大于当前全排列的最小的全排列(不妨假设字符集为正整数集):
- 当i>j, 则pi>pi+1,且k>j(∵pj+1>pj)
- 因为下标为j之前的部分没有发生改变,不妨忽略,记后缀部分为
p′=pj..pk...pn ,∵pj+1...pn为递减列,∴p′>pj(∗),其中pj(∗)是指以pj开头的任意后缀,当然后面的字符为pj+1...pn的全排列- pk为后续字符中大于pj的最小字符,∴下一个全排列应为pk(∗)
- 将pj与pk交换后,后面仍为递减列,将其翻转则为递增列,为这些字符组成的最小的全排列