0
点赞
收藏
分享

微信扫一扫

方法调用和递归程序的算法复杂度分析

源码之路 2022-04-19 阅读 72

方法调用

假定有以下代码段:

for (int count = 0; count < n; count++)
{
	printsum(count);
}

循环执行的次数乘上循环体的阶就可以得到整个循环的阶。而本例的循环体是一个方法调用,所以必须先确定这个方法的阶才能确定这个代码段的阶。假设每次调用该方法完成的功能是打印从1到n的和,那么该方法或许可以写成在这样的蛮力解法:

public void printsum(int count)
{
	int sum = 0;
	for (int i = 1; i < count; i++)
		sum += i;
	System.out.println(sum);
}

这样的话方法printsum的时间复杂度是多少呢?在程序中只有可执行语句才对时间复杂度起作用,所以方法中出来循环外,全部可执行语句的阶是O(1),而循环的阶是O(n),所以方法本身的时间复杂度是O(n)。计算最初调用该方法的循环的时间复杂度就是讲方法的复杂度乘以循环(方法)执行的次数,因此得到的结果是O(n2)。
我们不一定非要使用循环来计算从1到n的和,事实上累加同样可以用等差求和公式表示。现在改写printsum方法,看看时间复杂度有什么变化。

public viod printsum(int count)
{
	sum = count*(count+1)/2;
	System.out.println(sum);
}

现在,printsum方法的时间复杂度由O(1)阶的赋值语句和O(1)阶的打印语句构成。printsum方法的时间复杂度变成了O(1),这意味着调用该方法的循环现在由O(n2)变为O(n)。这样的修改带来了有效的改善,它再次表明了提供正确的代码和提供高效的代码是有差别的。

那么如果方法体由多个方法调用及循环组成,又会怎么样呢?考虑下面的代码,仍然使用了前面的printsum方法:

public void sample(int n)
{
	printsum(n);
	for (int count = 0; count < n; count++)
		printsum(count);
	for (int count = 0; count < n; count++)
	{
		for (int count2 = 0; count < n; count++)
		{
			System.out.println(count,ccount2);
		}
	}
}

最初使用临时变量作为参数调用printsum方法的阶是O(1),因为该方法的阶是O(1)。使用count作为参数调用printsum方法的for循环的阶是O(n),因为方法的阶是O(1)且循环执行了n次。嵌套的循环的阶是O(n2),因为外层循环共执行n次,而每次执行又会让内层循环执行n次。因为只关注主项,所以整个方法的阶是O(n2)。该示例方法的增长函数可以表示如下:

忽略常数项和所有的非主项,时间复杂度为O(n2).

递归算法效率分析

在分析循环时,我们要确定循环体的阶,再乘上循环执行的次数。递归算法的分析使用类似的思路。为了确定递归算法的阶,需要确定递归的阶(递归定义的次数),再乘以递归方法体的阶。考虑一个正整数求和的递归算法如下:

public int sum(int num)
{
	int result;
	if (num == 1)
		result = 1;
	else
		result = num + sum(num-1);
	return result;
}

用这个例子分析算法时间复杂度。因为要从整数1加到整数num,所以要累加的值的个数就是num。对于方法体而言,该递归方法执行一个加法操作,所以时间复杂度是O(1);每次调用递归方法时num的值都减1,所以递归方法被调用num次,因为递归的阶是O(n)。所以,由于方法体的阶是O(1),并且递归的阶是O(n),因此整个算法的阶是O(n)。

经典的汉诺塔问题,问题的大小自然就是盘子的个数,主要的操作就是将一个盘子从一个柱子移到另一个柱子。每次递归调用moveTower方法就会移动一个盘子,但是除了基础情形之外,每次递归调用都会导致这个方法再调用自身两次以上,并且每次调用时作为参数传入的待操作的盘子数只比前一次调用时少1个。所以,对1个盘子调用moveTower方法会移动1个盘子;对2个盘子调用moveTower方法就会移动3个盘子;对3个盘子调用moveTower方法就会移动7个盘子;对4个盘子调用moveTower方法就会移动15个盘子;等等。如果f(n)是这个问题的增长函数,则

与简洁优雅的代码实现相反,汉诺塔问题解决方案的执行效率极低。要求解n个盘子的汉诺塔难题,需要移动2n-1次盘子。所以汉诺塔算法的复杂度是O(2n)。这是指数阶的复杂度,当盘子数增大时,要移动的步数会按指数爆炸式地增长。

举报

相关推荐

0 条评论