对数最常出现的规律可概括为下列一般法则:
如果一个算法用常熟时间(O(1))将问题的大小削减为其一部分(通常是1/2),那么该算法就是O(logN)。另一方面,如果使用常数时间只是把问题减少一个常数的数量(如将问题减少1),那么这种算法就是O(N)的。
例1:折半查找(二分法)
条件: 给定一个整数X和整数A0,A1,...,A(N-1),后者已经预先排序并在内存中,求下标i使得Ai=X,如果X不在数据中,则返回i=-1。
思路:验证X是否是居中的元素。如果是,则答案就找到了。如果X小于居中元素,那么我们可以应用同样的策略于居中元素左边已排列的子序列;同理,如果X大于居中元素,那么我们检查数据的右半部分。
public static <AnyType extends Comparable<? super AnyType>> int binarySearch(AnyType a[],AnyType x){
int low=0,high=a.length-1;
while(low<=high){
int mid=(low+high)/2;
if(a[mid].compareTo(x)<0)
low=mid+1;
else if(a[mid].compareTo(x)>0)
high=mid-1;
else
return mid;//found
}
return NOT_FOUND;//NOT_FOUND is defined as -1
}
例2:欧几里得算法-计算最大公因数
两个整数的最大公因数(gcd)是同事整除两者的最大整数思路:假设M>=N(否则交换),算法连续计算余数为0为止,最后的非零余数就是最大公因数。(两个数的余数肯定比两个数的较小那个还要小,所以每次求余至少缩减一半。)
public static long gcd( long m,long n){
while(n!=0){
long rem = m % n;
m = n;
n = rem;
}
return m;
}
例三:幂运算
计算X^N(N-1次X自乘)
思路:N<=1是这种递归的基准情形。否则,若N是偶数,我们有X^N=X^(N/2) * X^(N/2),如果是偶数,我们有X^N=X^((N-1)/2) * X^((N-1)/2) * X。
例如,为了计算X^62,算法将如下运行,他只用到9次乘法。
X^3=(X^2)*X,
X^7=(X^3)^2 * X,
X^15=(X^7)^2 * X,
X^31=(X^15)^2 * X,
X^62=(X^31)^2
public static long pow( long x ,int n){
if(n==0) return 1;
if(n==1) return x;
if(isEven(n)) return pow(x*x,n/2);//偶数
else return pow(x*x,n/2)*x;
}