您的位置:首页 > 理论基础 > 数据结构算法

【数据结构】对数运行时间算法(折半查找、最大公约、幂运算)

2017-09-06 21:47 295 查看
已经知道,某些分治算法将会以O(NlogN)的时间复杂度运行,此外,对数复杂度的算法出现的一般规律可以概括为下面的一般的法则:

如果一个算法用常数(O(1))时间将问题的大小削减为其一部分(通常是1/2),那么这种算法就是O(log N)。

另一方面,如果使用常数时间只是把问题减少一个常数的数量(如将问题减少1),那么这种算法就是O(N)的

第一个例子是对于已经排好序(设为增序)的数组进行查找

1.折半查找(binary search)

问题叙述:给定一个X和整数A0,A1,...,AN-1,后者已经排序并且已经在内存中,求下标i是的Ai = X,如果X不在数据中,则返回-1

一个好的策略是验证X是否是居中的元素。然后进行折半查找。如果小于,同样的策略查找左边的序列,如果大于同样的策略查找右边的序列。

下面给出了折半查找的具体代码:

/**
* 每次循环使用两次比较用来执行标准的折半查找
* 找到时返回所求项的下标,找不到返回-1
* **/
template <typename Comparable>
int binarySearch(const vector<Comparable> &a, const Comparable & x)
{
int low = 0, high = a.size() - 1;
while (low <= high)
{
int mid = (low + high) / 2;
if(a[mid] < x)
low = mid + 1;
if(a[mid] > x)
high = mid - 1;
else
return mid;/**找到**/
}
return -1; /**未找到**/
}

2.欧几里得算法求最大公因数(gcd)

已知两个整数中的最大公因数是同时整除二者的最大整数,如gcd(15,20)=5

下面给出了求gcd复杂度为O(log N)的欧几里得算法的代码:

/**欧几里得算法**/
long long gcd(long long m,long long n)
{
while( n != 0)
{
long long rem = m % n;
m = n;
n = rem;
}
return m;
}上述算法的主要思想是:
通过连续计算余数直到余数是0为止,最后的非零余数就是最大公因数。

算法运行的时间复杂度仅仅是对数级别的原因是,我们可以证明,在两次迭代以后,余数最多是原始值得一半。这也就说明,迭代次数最多是2logN = O(logN)

有如下定理:

如果 M>N,则有M mod N < M/2

证明:存在两种情形,如果是N ≦ M/2, 则由于余数小于N,所以定理在这种情形下成立,另外一种情形是 N ≥ M/2,但是此时M仅仅含有一个N从而余数为 M-N < M/2

综上,定理成立。

3. 幂运算

计算幂运算的一种明显的算法是使用N -1次乘法进行自乘,但是有一种递归算法更好。

N ≦ 1时是递归的基准情形。否则,如果N 是偶数,就有 X^N = X^(N/2) * X^(N/2); 如果N 是奇数,就有 X^N = X ^((N-1)/2) * X ^((N-1)/2) * X

显然算法所需要的乘法次数最多是2logN,因为把问题分半最多需要两次乘法(如果N 是奇数)

具体实现的代码如下所示:

/**使用对数复杂度的递归计算幂运算**/
long long pow(long long x, int n)
{
if(n == 0)
return 1;
if( n % 2 == 1) /**n是偶数**/
return pow(x * x, n/2);
else
return pow(x * x, n/2) * x;
/**上一行也可以写为如下形式:
* return pow(x, n-1) * x
* */
}

至此,本篇结束。
END.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: