从算法学起C语言--河内之塔

转载请注明出处,珍惜下劳动成果~~~

背景说明:

河内之塔(Towers of Hanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市;1883年法国数学卷Edouard Lucas曾提及这个故事.据说创世纪时Benares有一座波罗教塔,是由三支钻石棒所支撑,开始时神在第一根棒上放置64个由上至下依由小至大一次排列的金盘,并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕时,此塔将毁损,而也就是世界末日来临之时.

                                                                                                                                                                                                                                         ----译自  世界经典算法

大天朝白话文:

就是说这有三根棒子,第一个棒子上有几个盘子(数量未定),盘子是大的在下边,小的在上边,依次排列,而规则就是你要将所有的盘子按同样的顺序移到第三根上,过程中,不管在哪个棒子上,都要遵守大盘子在下边,小盘子在上边的规则.


这个我在自学数据结构的时候(不要问我为什么C还不会就学数据结构,大学的C课程,你脑补一下原因,我当时以为我学会C了.),在递归里是很经典的例子.还依稀记得那一小节的名字是分治法思想.其实说白了就是递归的实现.

首先自己动手实现一下,我们要找规律.

当有一个盘子时,移动的次序的A->C.

当有两个盘子时,A->B,A->C,B->C.

当有三个盘子时,A->C,A->B,C->B,A->C,B->A,B->C.A->C.

当有四个盘子时,A->B,A->C,B->C,A->B,C->A,C->B,A->B,A->C,B->C,B->A,C->A,B->C,A->B,A->C,B->C.

以此类推.

首先根据数学的归纳总结思想,你会发现一个有趣的现象,1的时候A->C,2的时候A->B,3的时候A->C,4的时候A->B...........奇数A->C,偶数A->B.好,先记下.(笔算加脑补运行了很多个盘子,发现确实这样.)

然后截取一下过程,3个的时候,A->C,A->B,C->B,4个的时候,A->B,A->C,B->C,A->B,C->A,C->B,A->B.截取的过程发现,都是先把除最后一个盘子之外的所有盘子按序放到了第二个棒子上,然后将最大的盘子放到第三个棒子上,之后,就相当于第三个棒子还是没有东西,因为它上面放的是最大的盘子,任何盘子都能放到它的上面,这样问题就变成了,有三个棒子,第二个棒子上有按序放置的一叠盘子,要把它们全部移动到第三个棒子上,相当于1,2棒子互换了下位置.问题不变而已.典型的递归操作!

然后想想递归的思想,把大量的数据处理简化成小量的数据处理,即把大规模的处理分成多个小规模的处理(我比较讨厌记条条框框,所以理解意思就好,我不知道是不是对的上教条).根据这个,我们尝试写代码.首先写一个盘子的时候:

void hanoi(int n,char A,char B,char C)
{
	if (n == 1)
	{
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
	}
}
一个盘子的时候很简单,就是移动到第三根棒子上就可以,然后我们尝试写2个盘子的时候:

void hanoi(int n,char A,char B,char C)
{
	if (n == 1)
	{
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
	}else if (n == 2)
	{
		printf("将盘子 %d 从 %c 移到 %c \n", (n-1) , A ,B);
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
		printf("将盘子 %d 从 %c 移到 %c \n",n-1 , B ,C);
	}
}

发现一个问题,当n=2的时候,在移动第一个盘子的过程仅仅是将参数改变,结合我们前边提到的发现的那个小规律,改造下代码,写成递归的形式:

void hanoi(int n,char A,char B,char C)
{
	if (n == 1)
	{
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
	}else if (n == 2)
	{
		hanoi(n-1,A,C,B);
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
		hanoi(n-1,B,A,C);
	}
}

下面我们去掉else中的条件,让他成为一个适合所有n的函数,以此检验我们将问题简化后的思想是不是具有广泛性:

void hanoi(int n,char A,char B,char C)
{
	if (n == 1)
	{
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
	}else 
	{
		hanoi(n-1,A,C,B);
		printf("将盘子 %d 从 %c 移到 %c \n",n , A ,C);
		hanoi(n-1,B,A,C);
	}
}

此外我们还要加上main函数作为入口:

#include 
#include 

void hanoi(int ,char ,char ,char);

int main()
{
	int n ;
	printf("请输入盘子数量 : n>0");
	scanf("%d",&n);//这里scanf函数要传入个地址
	hanoi(n,'A','B','C');
	system("pause");
	return 0;
}

ok,看看执行后的结果:

请输入盘子数量 : n>05
将盘子 1 从 A 移到 C
将盘子 2 从 A 移到 B
将盘子 1 从 C 移到 B
将盘子 3 从 A 移到 C
将盘子 1 从 B 移到 A
将盘子 2 从 B 移到 C
将盘子 1 从 A 移到 C
将盘子 4 从 A 移到 B
将盘子 1 从 C 移到 B
将盘子 2 从 C 移到 A
将盘子 1 从 B 移到 A
将盘子 3 从 C 移到 B
将盘子 1 从 A 移到 C
将盘子 2 从 A 移到 B
将盘子 1 从 C 移到 B
将盘子 5 从 A 移到 C
将盘子 1 从 B 移到 A
将盘子 2 从 B 移到 C
将盘子 1 从 A 移到 C
将盘子 3 从 B 移到 A
将盘子 1 从 C 移到 B
将盘子 2 从 C 移到 A
将盘子 1 从 B 移到 A
将盘子 4 从 B 移到 C
将盘子 1 从 A 移到 C
将盘子 2 从 A 移到 B
将盘子 1 从 C 移到 B
将盘子 3 从 A 移到 C
将盘子 1 从 B 移到 A
将盘子 2 从 B 移到 C
将盘子 1 从 A 移到 C
请按任意键继续. . .


完美执行!

你可能感兴趣的:(C/C++)