运行时间具有对数性质的几种算法
对分查找算法
给定一个整数 X 和整数 A0A_{0}A0, A1A_{1}A1, A2A_{2}A2……, AN−1A_{N-1}AN−1,后者已经预先排序并在内存中,求使得 AiA_{i}Ai=X 的下标 i ;如果 X 不在数据中,则返回-1.
int BinarySearch( const ElementType A[], ElementType x, int N)
{
int low, mid, high;
low = 0; high = N-1;
while( low <= high )
{
mid=( low + high )/2;
if( A[mid] < x)
low = mid + 1;
else
{
if( A[mid] > x)
high = mid -1;
else
return mid;
}
}
return -1;
}
显然,每次迭代再循环内的所有工作花费为O(1), 因此分析需要确定循环的次数。循环从 high - low = N-1 开始;到 high - low >= -1 结束。每次循环后 high - low的值减少一半,于是循环次数最多为[ log(N-1) ] + 2。
例如,若 high - low = 128, 则在各次迭代后 high - low 的最大值为64,32,16,8,4,2,1,0,-1。
因此,运行时间是O( logN )。
欧几里得算法
给定两个整数 M , N ,求 M , N 的最大公因数。
unsigned int Gcd( unsigned int M, unsigned int N )
{
unsigned int Rem;
while( N > 0 )
{
Rem = M % N;
M = N;
N = Rem;
}
return M;
}
因为余数不会超过原始值的一半,所以运行时间也是O( logN )。
冥运算
计算xnx^nxn。(假设机器能够存储足够大的数)
注:如果n是偶数,则xnx^nxn = xn2x^\frac{n}{2}x2n * xn2x^\frac{n}{2}x2n,如果n是奇数,那么 xnx^nxn = xn−12x^\frac{n-1}{2}x2n−1 * xn−12x^\frac{n-1}{2}x2n−1 * x。
long int Pow( long int x, unsigned int n)
{
if( n == 0 )
return 1;
if( n == 1 )
return x;
if( IsEven ( n ) ) /* 判断是否是偶数 */
return Pow( x * x, n / 2 ); /* 用x*x做参数,能提高效率 */
else
return Pow( x * x, n / 2) * x;
}
例如,为了计算 x62x^{62}x62,算法将如下进行,它只用到9次乘法:
x3x^3x3 = ( x2x^2x2 ) * x,
x7x^7x7 = (x3)2{ (x^3) }^2(x3)2 * x,
x15x^{15}x15 = (x7)2{ (x^7) }^2(x7)2 * x
x31x^{31}x31 = (x15)2{( x^{15} )}^2(x15)2 * x,
x62x^{62}x62 = (x32)2{( x^{32} )}^2(x32)2
显然,所需要的乘法次数最多是2logn; 因为把问题分半最多需要两次乘法(如果n是奇数)。