递归的概念
递归在维基中是这样定义的:在计算机科学中递归是指一种通过重复将问题分解为同类的子问题而解决问题的方法。下是另一个可能更有利于理解递归过程的解释:
①、我们已经完成了吗?如果完成了,返回结果。如果没有这样的终止条件,递归将会永远地继续下去。
②、如果没有,则简化问题,解决较容易的问题,并将结果组装成原始问题的解决办法。然后返回该解决办法。
其实,处理递归调用与处理任何其他函数调用没有什么不同,要点在于,递归调用将一直进行到基准情况出现为止。递归有四个基本法则:
①、基准情形:必须总有某些基准情形,它不用递归就能求解。
②、不断推进:对于那些要被递归求解的情形,递归调用必须总能朝着基准情形推进。
③、设计法则:设计递归程序时不要试图追踪大量的递归调用,假设所有的递归调用都能运行。
④、合成效益法则:切勿在不同的递归调用中做重复性的工作,典型的例子就是递归计算斐波那契数列。
汉诺塔问题
汉诺塔问题解法的基本思想就是递归:假设有A、B、C三个塔,A塔有n块盘,目标是把这些盘全部移到C塔,每次只能移动一个盘,大盘不能在小盘之上。
基准情形:塔上只剩最后一个盘子的时候,直接将其从所在的塔上移动到目标塔上。
不断推进:先把A塔顶部的n-1块盘移动到B塔,再把A塔剩下的最后一个大盘移到C,最后把B塔的n-1块盘移到C。每次移动多于一块盘时,再次使用这种算法来移动。
设计法则:当A塔有两个盘子的时候可以借助另一个塔完成移动,当A塔有三个盘子的时候也是通过借助第三个塔完成移动,当A塔有n个盘子的时候同样可以通过第三个塔完成移动。
void hannoi(int n, char from, char buffer, char to)
{
if (n == 1)
{
cout << "Move disk " << n << " from " << from << " to " << to << endl;
}
else
{
hannoi(n - 1, from, to, buffer);
cout << "Move disk " << n << " from " << from << " to " << to << endl;
hannoi(n - 1, buffer, from, to);
}
}
分析汉诺塔时间复杂度:问题规模为n,T(n)为问题规模所需步骤,
T(n)=1+2T(n-1)//规模为n-1时要经过两次,所以为2T(n-1)
= 1 + 2[ 1 + 2T(n - 2)]
= 3 + 4T(n - 2)
= 3 + 4[1 + 2T(n - 3)]
= 7 + 8T(n - 3)
......
= 2^k - 1 + 2^kT(n-k)
当n-k=1时,得到k=n-1,
T(n)=2^(n - 1) + 2^(n-1) T(1) //其中T(1)=1
T(n)=2^n
综上:汉诺塔时间复杂度为O(2^n)。
递归与for循环
for循环算法可以使用递归来替代,如下面的判断一个数组是否为递增问题。但递归算法如果使用循环来替代有的情况可能会很麻烦或者不能实现,递归不应该作为简单for循环的替代物。
//判断一个数组是否为递增:for循环
bool IsIncreas(int ary[], unsigned int size)
{
for (unsigned int i = 1; i < size; i++)
{
if (ary[i] < ary[i - 1])
return false;
}
return true;
}
//判断一个数组是否为递增:递归算法
bool IsIncrease(int ary[], unsigned int size)
{
if (size == 1)
return true;
if (size == 2)
return ary[1] > ary[0];
if (ary[size - 1] > ary[size - 2])
return IsIncrease(ary, size - 1);
else
return false;
}