运行时间具有对数性质的几种算法

本文介绍了三种具有对数时间复杂度的算法:对分查找算法、欧几里得算法和冥运算。对分查找算法通过不断缩小搜索范围来查找元素;欧几里得算法利用辗转相除法求最大公约数;冥运算则采用分治法高效计算幂次。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行时间具有对数性质的几种算法

对分查找算法

给定一个整数 X 和整数 A0A_{0}A0, A1A_{1}A1, A2A_{2}A2……, AN−1A_{N-1}AN1,后者已经预先排序并在内存中,求使得 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}x2n1 * xn−12x^\frac{n-1}{2}x2n1 * 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是奇数)。

### 不同算法复杂度的计算方法 #### 时间复杂度计算方式及示例 时间复杂度衡量的是一个算法执行所需的时间随着输入规模的增长而增长的速度。通常使用大O记号来描述最坏情况下的性能。 对于简单循环结构: ```cpp for (int i = 0; i < n; ++i) { // 执行一些操作 } ``` 上述代码中的循环会执行`n`次,因此其时间复杂度为 O(n)[^1]。 嵌套循环则会使复杂度增加到更高阶: ```cpp for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { // 执行某些操作 } } ``` 这里有两个相互独立变化的参数 `m` 和 `n`, 如果两者相等,则该双重循环的时间复杂度可以简化成 O(n²),即平方级别[^2]。 递归函数的时间复杂度取决于每次调用自身的次数以及减少问题规模的方式。例如经典的二分查找算法,在最佳情况下能够将搜索范围减半直到找到目标或者确认不存在,这样的过程可以用对数级表示,也就是 O(log₂n)[^3]。 #### 空间复杂度计算方式及示例 空间复杂度关注于程序运行期间所消耗的最大内存资源数量。这不仅限于显式的数组或其他数据容器分配了多少存储单元;还包括隐含使用的栈帧大小——特别是当存在大量递归调用这一点尤为重要。 考虑下面的例子: ```cpp void function(int n, int* data){ int temp[n]; // 动态创建了一个长度为'n'的新数组 ... } ``` 这段代码中声明了一个局部数组`temp[]`,它占据了连续的一片内存区域,这部分额外开销构成了此函数的空间复杂度的一部分,具体来说就是 O(n)[^1]。 再看另一个例子: ```cpp void recursiveFunction(int level){ if(level == 0) return; int localVariable; recursiveFunction(level - 1); } ``` 每当进入更深一层递归都会新增加若干个局部变量实例化所需的堆栈空间,假设每层只增加了固定量的辅助空间,那么整个递归链路下来总共需要 O(d) 的附加空间,其中 d 表示递归深度。 综上所述,无论是分析时间还是空间上的需求,都应当仔细考察各个组成部分的影响因素,并据此推导出合理的估计值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值