#include <stdio.h>
/**
*递归处理汉诺塔
*/
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit)
{
if (paraN == 0) return;
else
{
hanoi(paraN - 1, paraSource, paraTransit, paraDestination);
printf("%c -> %c\n", paraSource, paraDestination);
printf("%p -> %p\n", ¶Source, ¶Destination);
hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
}
}
/**
*测试函数,使用两个例子进行测试。将三根柱子设为a,b,c。
*/
void hanoiTest()
{
printf("--- addToTest begins. ---\n");
printf("2 plates\n");
hanoi(2, 'A', 'B', 'C');
printf("3 plates\n");
hanoi(3, 'A', 'B', 'C');
printf("---- addToTest ends. ---\n");
}
void main()
{
hanoiTest();
}
要想将N个方块从A移动到C,就要
①将N-1个方块从A移动到B(利用递归解决这一步);
②将方块”N“从A移动到C(因为此时N上方的方块全在B柱子上,由于步骤①的实现,方块N可以再A和C之间畅通无阻);
③将剩余的N-1个方块从B移动到N的上方,也就是将N-1个方块从B移动到C(如何实现这一步?这一步不就是和步骤①一模一样吗?)
分析:
1、自顶向下,逐项求精
汉诺塔问题需要运用到递归的方法来解决,递归就是要将问题分解为规模较小但类型相同的子问题。自顶向下可以看作一颗递归树,第一层一个节点,下面按2的倍数增加。最后这些解合并起来就是问题最终的解。
2、递归,分治
分治法是一种较为常用的算法。它是指将问题分解为规模较小的子问题,通过递归的方式进行分治。分治法需要注意划分的范围。而递归需要注意递归函数的出口与边界条件。汉诺塔问题就需要使用递归与分治的思想。出口函数一般设置为n=1的时候。
3、形参与实参
用通俗的话来讲,形参相当于占一个坑,告诉这个函数有哪些坑在调用的时候需要填上。而实参就是我们需要实际解决问题的大小。
4、有意义、规范的标识符
有意义、规范的标识符一般用英文单词的缩写或是i,j此类为大部分人所熟知的迭代标识符。可以提高代码的可读性。
5、时间复杂度
虽然递归调用时我们对其进行了分治,但是子问题的总规模不变,所以递归树的每一层总的时间都为2cn,是按2的倍数递增,所以时间复杂度为o(2^n)。
6、递归栈
计算机在执行指令时,一次只能执行一条指令,所以,需要用到栈的结构。简单来说就是先将指令入栈,在递归到不能再向下分解时,开始执行指令,即将栈顶指令出栈。
7、空间复杂度
由于在递归过程中,我们需要使用一个递归栈来保存每个递归调用的状态。因为递归栈的深度等于递归调用的层数,所以空间复杂度为 O(n)。