快速幂
求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 交换后,后面仍为递减列,将其翻转则为递增列,为这些字符组成的最小的全排列