数据结构 汉诺塔

文章介绍了汉诺塔问题的递归解法,包括自顶向下的问题分解,函数调用、递归与分治策略,以及形参和实参的概念。同时,讨论了问题的时间复杂度为O(2^n)和空间复杂度为O(n),并强调了递归栈在解决问题中的作用。
摘要由CSDN通过智能技术生成

用f(n, a, b, c)表示要求解的问题,其含义是有a、b、c三根棒和n只盘,且这n个盘叠放在a棒上,依次叠放为大盘在下,小盘在上。借助b棒将n只盘从a棒移到c棒上。每次只移一个盘,在移动时保持大盘在下,小盘在上。将f(n, a, b, c)转化分解为如下三个子问题:①f(n - 1, a, c, b),即将a棒上面的n-1个盘移到b棒,借助c棒。②move(a, c),即将在a棒上的一个盘移到c棒。③f(n - 1, b, a, c),即将b棒上面的n-1个盘移到c棒,借助a棒。

 

#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("%ld\r\n",&paraSource);
        printf("%ld\r\n",&paraTransit);
        printf("%ld\r\n",&paraDestination);

		hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
	}
}

void hanoiTest(){
    printf("---- begins. ----\n");

	printf("2 plates\n");
	hanoi(2, 'A', 'B', 'C');

	printf("3 plates\n");
	hanoi(3, 'A', 'B', 'C');
	
	printf("4 plates\n");
	hanoi(4, 'A', 'B', 'C');
	
	printf("5 plates\n");
	hanoi(5, 'A', 'B', 'C');

    printf("---- ends. ----\n");
}

void main(){
	hanoiTest();
}

运行结果

---- begins. ----
2 plates
A -> C
548948847819
548948847817
548948847818
A -> B
548948847883
548948847881
548948847882
C -> B
548948847819
548948847817
548948847818
3 plates
A -> B
548948847755
548948847753
548948847754
A -> C
548948847819
548948847817
548948847818
B -> C
548948847755
548948847753
548948847754
A -> B
548948847883
548948847881
548948847882
C -> A
548948847755
548948847753
548948847754
C -> B
548948847819
548948847817
548948847818
A -> B
548948847755
548948847753
548948847754

问题分析

1.自顶向下,逐渐求精

对要完成的任务进行分解,先对最高层次中的问题进行定义、设计、编程和测试,而将其中未解决的问题作为一个子任务放到下一层次中去解决。这样逐层、逐个地进行定义、设计、编程和测试,直到所有层次上的问题均由实用程序来解决,就能设计出具有层次结构的程序。 
自顶向下是将复杂、大的问题划分为小问题,找出问题的关键、重点所在,然后用精确的思维定性、定量地去描述问题。 逐步求精是将现实世界的问题经抽象转化为逻辑空间或求解空间的问题。复杂问题经抽象化处理变为相对比较简单的问题。经若干步抽象处理,最后到求解域中只是比较简单的编程问题。

2.函数调用、递归与分治

编写递归函数时,必须告诉它何时停止递归。正因为如此,每个递归函数都有两部分:基线条件和递归条件。递归条件指的是函数调用自己,而基线条件则指的是函数不再调用自己,从而避免形成无限循环。分治步骤:1. 找出基线条件,这种条件必须尽可能简单。2. 不断将问题分解(或者说缩小规模),直到符合基线条件。

3.形参与实参

在函数调用过程中,我们使用形参来表示函数的输入参数,而实参则是在函数调用时提供的具体数值。在汉诺塔问题中,形参 n、a、b 和 c 分别表示要移动的圆盘数和三个柱子。在函数调用时,我们需要提供实际的圆盘数和三个柱子的具体名称,如 hanoi(3, ‘A’, ‘B’, ‘C’)。

4.有意义,规范的标识符

在编写程序时,使用有意义、规范的标识符可以使代码更易于理解和维护。在汉诺塔问题中,我们可以使用有意义的变量名和函数名,如 paraN,parasource和 hanoi()。这些标识符可以更好地表达程序的含义,并提高代码的可读性。

5.时间复杂度

  汉诺塔问题的时间复杂度为 O(2^n),其中 n 是圆盘的数量。这是因为在每次递归调用中,我们需要移动 2 个圆盘。此外,在解决汉诺塔问题的过程中,我们需要进行 n 次递归调用,每次递归调用需要进行 3 次移动操作。因此,总的移动次数为 3^(n - 1),这是一个指数级别的复杂度。在实际应用中,当圆盘数量较大时,解决汉诺塔问题的时间复杂度会变得非常高。

6.递归栈

 递归函数的调用过程需要使用递归栈来保存每个函数调用的状态。在汉诺塔问题中,每个递归调用会创建一个新的函数调用帧,并将其压入递归栈中。当递归调用返回时,对应的函数调用帧会被弹出,恢复到上一层调用的状态。因此,在解决汉诺塔问题的过程中,递归栈的深度等于递归调用的层数,即 n。

7.空间复杂度

汉诺塔问题的空间复杂度为 O(n),其中 n 是圆盘的数量。这是因为在解决问题的过程中,我们需要使用一个递归栈来保存每个递归调用的状态。由于递归栈的深度等于递归调用的层数,因此空间复杂度为 O(n)。除了递归栈之外,我们还需要使用一些额外的变量来保存问题的状态,但这些变量的数量与圆盘的数量无关,因此不会影响空间复杂度。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值