(重要)递归(含代码执行过程解释)
- 格式:doc
- 大小:238.50 KB
- 文档页数:9
C语言递归实现1到n的和简介递归是一种常用的编程技巧,它通过函数自身的调用来解决问题。
在C语言中,递归可以用于实现各种算法和数据结构。
本文将介绍如何使用递归来计算1到n的和,通过详细的代码解释和示例演示,帮助读者理解递归的原理和使用方法。
递归的基本原理递归是一种通过函数自身的调用来解决问题的方法。
在递归中,函数会不断地调用自身,直到满足某个终止条件才停止调用。
递归可以分为两个阶段:递归调用和递归返回。
递归调用是指函数在执行过程中,自己调用自己。
在每次递归调用时,函数会使用不同的参数值,以便在每次调用中解决不同的子问题。
递归调用将问题分解为更小的子问题,直到达到终止条件。
递归返回是指函数在满足终止条件后,通过返回值将结果传递给上一层调用。
通过不断返回结果,最终得到整个问题的解。
递归实现1到n的和下面是使用递归实现1到n的和的C语言代码:#include <stdio.h>int sum(int n) {if (n == 1) {return 1;} else {return n + sum(n - 1);}}int main() {int n;printf("请输入一个正整数n:");scanf("%d", &n);printf("1到%d的和为:%d\n", n, sum(n));return 0;}在上面的代码中,我们定义了一个名为sum的递归函数,它接受一个整数参数n,并返回1到n的和。
在函数内部,我们使用了一个if-else语句来判断是否满足终止条件。
当n等于1时,递归终止,直接返回1。
否则,递归调用sum函数,并将n减1作为参数传入,然后将递归调用的结果与n相加返回。
在main函数中,我们首先从用户输入获取一个正整数n,然后调用sum函数计算1到n的和,并将结果打印出来。
递归的执行过程为了更好地理解递归的执行过程,我们以计算1到5的和为例,来逐步分析递归的调用和返回过程。
python递归实验报告Python递归实验报告引言:递归是计算机科学中一种重要的编程技术,它在解决问题时能够简化代码逻辑,并提高代码的可读性和可维护性。
Python作为一种高级编程语言,提供了强大的递归支持,本文将通过实验来探讨Python递归的特性和应用。
一、递归的概念与原理递归是一种通过调用自身的方式解决问题的方法。
在递归过程中,问题被分解为更小的子问题,直到子问题足够简单可以直接求解。
递归的基本原理是将一个大问题转化为一个或多个与原问题相似但规模更小的子问题,通过递归调用解决子问题,最终得到原问题的解。
二、递归的实现方式在Python中,递归可以通过函数调用自身来实现。
递归函数通常包含两个部分:基准情况和递归情况。
基准情况是递归函数的结束条件,当满足基准情况时,递归函数将不再调用自身,而是返回一个特定的值。
递归情况是指递归函数在未满足基准情况时,调用自身来处理更小规模的子问题。
三、递归的应用场景1. 阶乘计算阶乘是指从1到给定数之间所有整数的乘积。
递归可以很方便地实现阶乘计算,如下所示:```pythondef factorial(n):if n == 0:return 1else:return n * factorial(n-1)```2. 斐波那契数列斐波那契数列是指从第三项开始,每一项都等于前两项之和。
递归可以很容易地实现斐波那契数列的计算,如下所示:```pythondef fibonacci(n):if n <= 1:return nelse:return fibonacci(n-1) + fibonacci(n-2)```3. 文件夹遍历递归还可以用于文件夹的遍历,通过递归调用实现对文件夹内所有文件的查找和处理。
例如,可以编写一个函数来计算指定文件夹下所有文件的总大小:```pythonimport osdef get_folder_size(folder):size = 0for item in os.listdir(folder):item_path = os.path.join(folder, item)if os.path.isfile(item_path):size += os.path.getsize(item_path)elif os.path.isdir(item_path):size += get_folder_size(item_path)return size```四、递归的优缺点递归的优点在于能够简化问题的解决过程,提高代码的可读性和可维护性。
递归的作用与意义递归是一种常见的编程技术,它在解决问题时具有重要的作用与意义。
递归是指一个函数或过程在执行过程中不断调用自身的过程。
通过递归,可以将复杂的问题分解成更小的子问题,并通过解决子问题来解决原始问题。
递归的作用之一是简化问题。
对于一些复杂的问题,我们可以使用递归的方式将其划分为更小的子问题。
这样一来,我们只需要解决每个子问题,然后将它们的解合并起来,就能得到原始问题的解。
递归的这种特性使得问题的解决变得更加清晰和简明。
递归还可以提高代码的可读性和可维护性。
使用递归的方式可以将代码分解成多个独立的函数,每个函数只负责解决一个子问题。
这样一来,我们可以更加专注地思考每个子问题的解决方案,而不需要同时考虑多个问题。
这种模块化的设计使得代码更易于理解和修改,从而提高了代码的可读性和可维护性。
除了简化问题和提高代码的可读性和可维护性之外,递归还可以解决一些特定的问题。
例如,在树的遍历中,递归可以帮助我们实现前序遍历、中序遍历和后序遍历等操作。
在图的搜索中,递归可以帮助我们实现深度优先搜索和广度优先搜索等算法。
递归的这种特性使得它成为解决这些问题的有效工具。
然而,递归也存在一些潜在的问题和限制。
首先,递归的实现需要消耗额外的内存空间,因为每次递归调用都需要保存函数的状态和局部变量。
如果递归的层数很深,可能会导致栈溢出的问题。
其次,递归的效率通常较低,因为每次递归调用都需要进行函数的调用和返回操作,而这些操作会带来额外的开销。
因此,在一些性能要求较高的场景中,可能需要考虑使用其他的解决方案。
递归在编程中具有重要的作用与意义。
它可以简化问题的解决过程,提高代码的可读性和可维护性,并解决一些特定的问题。
然而,递归也存在一些潜在的问题和限制。
因此,在使用递归时需要仔细考虑问题的性质和场景的要求,以确保递归的有效性和有效性。
只有在合适的场景下,递归才能发挥其最大的作用和意义。
希望通过本文的介绍,读者能够更好地理解递归的作用与意义,并在实际的编程中灵活运用。
第一章编译程序是一种程序,它把高级语言编写的源程序翻译成与之在逻辑上等价的机器语言或汇编语言的目标程序。
一个高级语言程序的执行通常分为两个阶段,即编译阶段和运行阶段。
如果编译生成的目标程序是汇编语言形式,那么在编译与运行阶段之间还要添加一个汇编阶段。
解释程序也是一种翻译程序,它将源程序作为输入,一条语句地读入并解释执行。
解释程序与编译程序的主要区别是:编译程序是将源程序翻译成目标程序后再执行该目标程序,而解释程序则是逐条读出源程序中的语句并解释执行,即在解释程序的执行过程中并不源程序产生目标程序。
编译过程可以划分成五个阶段:词法分析阶段、语法分词法分析器析阶段、语义分析和中间代码生成阶段、优化阶段和目单词符号标代码生成阶段。
词法分析的任务是对构成源程序的字语法分析器表出符串进行扫描和分解,根据语言的词法规则识别出一个语法单位个具有独立意义的单词;语法分析的任务是在词法分析格错语义分析与的基础上,根据语言的语法规则(文法规则)从单词符中间代码生成器管处号串中识别出各种语法单位并进行语法检查;语义分析四元式理和中间代码生成阶段的任务是首先对每种语法单位进行理优化静态语义检查,然后分析其含义,并用另一种语言形式四元式来描述这种语义即生成中间代码;优化的任务是对前阶目标代码生成器段产生的中间代码进行等价变换或改造,以期获得更为目标程序高效(节省时间和空间)的目标代码;目标代码生成阶段的任务是把中间代码(或经优化处、理之后)变换成特编译程序结构示意图定机器上的机器语言程序或汇编语言程序,实现最终的翻译工作。
自编译:用某种高级语言书写自己的编译程序。
交叉编译:指用A机器上的编译程序来产生可在B机器上运行的目标代码。
自展:首先确定一个非常简单的核心语言L0,然后用机器语言或汇编语言书写出它的编译程序T0:再把语言L0扩充到L1,此时有L0L1,并用L0编写L1的编译程序T1(即自编译)。
移植:指A机器上的某种高级语言的编译程序稍加改动后能够在B机器上运行。
递归函数详细讲解
(实用版)
目录
1.递归函数的定义和特点
2.递归函数的执行过程
3.递归函数的应用实例
4.递归函数的优缺点
正文
递归函数是一种在函数体内部调用自身的函数。
它能够实现一些具有重复性的任务,通过将大问题分解成小问题来解决问题。
递归函数的特点是函数体内部存在一个递归调用,通常是一个条件判断和一个递归调用组成。
递归函数的执行过程是先判断条件,如果满足条件就进行递归调用,否则执行其他操作。
递归函数在实际应用中非常广泛。
例如,计算阶乘、汉诺塔问题、斐波那契数列等都是典型的递归函数应用实例。
这些例子中,递归函数通过将大问题分解成小问题,然后通过解决小问题来解决大问题。
递归函数的优点是可以将复杂的问题简化为较小的、更易于解决的问题,而且递归函数的代码通常比较简洁。
但是,递归函数也存在一些缺点。
首先,递归函数的运行效率较低,因为需要进行多次函数调用和返回。
其次,递归函数的深度过大可能导致栈溢出,造成程序崩溃。
总的来说,递归函数是一种非常有用的编程技巧,能够解决许多复杂的问题。
第1页共1页。
在Java中,return是一个关键字,用于指示方法返回值或中断方法的执行。
它被广泛用于方法的定义和控制流程的处理。
本文将深入探讨return在Java中的用法,并分析其在不同情境下的实际应用。
1. return的基本用法在Java中,return的基本用法是指示方法返回值。
当方法声明了返回类型时,使用return可以将指定类型的值返回给方法的调用者。
一个求和的方法可以如下定义:```javapublic int sum(int a, int b) {return a + b;}```在上面的例子中,return关键字将a和b相加的结果返回给调用sum 方法的地方。
这样,调用者就可以得到这个求和的结果,并进行后续的处理。
2. 在控制流程中的应用除了作为方法的返回值,return还常用于控制流程中,比如在条件判断或循环中提前结束方法的执行。
我们可以在一个方法中使用return 来检查某个条件是否满足,如果不满足就立即结束方法的执行,并返回到方法的调用者处。
```javapublic void processList(List<String> list, String target) {for (String item : list) {if (item.equals(target)) {System.out.println("Found it!");return;}}System.out.println("Not found.");}```在上面的例子中,如果在list中找到了与target相等的元素,方法就会立即打印"Found it!"并结束执行。
否则,继续遍历list直到结束,打印"Not found."。
3. return在递归调用中的应用在递归调用中,return也扮演着重要的角色。
递归调用是指一个方法在执行过程中调用了自身,常见于解决树的遍历、阶乘计算和斐波那契数列等问题。
递归算法及经典递归例⼦代码实现递归(recursion):程序调⽤⾃⾝的编程技巧。
递归满⾜2个条件:1)有反复执⾏的过程(调⽤⾃⾝)2)有跳出反复执⾏过程的条件(递归出⼝)递归例⼦:(1)阶乘n! = n * (n-1) * (n-2) * ...* 1(n>0)//阶乘int recursive(int i){int sum = 0;if (0 == i)return (1);elsesum = i * recursive(i-1);return sum;}(2)河内塔问题//河内塔void hanoi(int n,int p1,int p2,int p3){if(1==n)cout<<"盘⼦从"<<p1<<"移到"<<p3<<endl;else{hanoi(n-1,p1,p3,p2);cout<<"盘⼦从"<<p1<<"移到"<<p3<<endl;hanoi(n-1,p2,p1,p3);}}(3)全排列从n个不同元素中任取m(m≤n)个元素,按照⼀定的顺序排列起来,叫做从n个不同元素中取出m个元素的⼀个排列。
当m=n时所有的排列情况叫全排列。
如1,2,3三个元素的全排列为:1,2,31,3,22,1,32,3,13,1,23,2,1//全排列inline void Swap(int &a,int &b){int temp=a;a=b;b=temp;}void Perm(int list[],int k,int m){if (k == m-1){for(int i=0;i<m;i++){printf("%d",list[i]);}printf("n");}else{for(int i=k;i<m;i++){Swap(list[k],list[i]);Perm(list,k+1,m);Swap(list[k],list[i]);}}}(4)斐波那契数列斐波纳契数列,⼜称黄⾦分割数列,指的是这样⼀个数列:1、1、2、3、5、8、13、21、……这个数列从第三项开始,每⼀项都等于前两项之和。
java递归详解递归是一种常见的编程技巧,它在解决问题时通过调用自身来实现。
在Java中,递归是一种强大而灵活的工具,可以用于解决各种问题。
本文将详细介绍Java递归的原理、应用场景以及一些注意事项。
首先,让我们来了解递归的原理。
递归函数是一种特殊的函数,它在执行过程中会调用自身。
递归函数通常包含两个部分:基本情况和递归调用。
基本情况是递归函数停止调用自身的条件,而递归调用是递归函数在满足基本情况之前一直调用自身。
递归函数的执行过程可以用一个栈来模拟。
每次递归调用时,函数的局部变量和参数都会被保存在栈中,直到满足基本情况时才会逐个弹出栈。
这种栈的结构被称为递归栈。
递归在解决问题时具有很大的灵活性。
它可以用于解决各种问题,如计算阶乘、斐波那契数列、二叉树遍历等。
下面我们以计算阶乘为例来说明递归的应用。
计算阶乘是一个经典的递归问题。
阶乘的定义是n的阶乘等于n乘以(n-1)的阶乘,其中0的阶乘定义为1。
我们可以使用递归函数来计算阶乘。
```javapublic class Factorial {public static int factorial(int n) {if (n == 0) {return 1;} else {return n * factorial(n - 1);}}public static void main(String[] args) {int n = 5;int result = factorial(n);System.out.println("The factorial of " + n + " is " + result);}}```在上面的代码中,factorial函数是一个递归函数。
当n等于0时,满足基本情况,函数返回1。
否则,函数调用自身,并将n减1作为参数传递给递归调用。
最终,递归函数的返回值是n乘以(n-1)的阶乘。
递归函数的使用需要注意一些问题。
js 递归写法JavaScript递归写法是一种非常重要的编程技巧。
通过递归实现可以使代码更简洁、优雅。
接下来,我们将一一详细解析JS递归写法。
一、递归的概念递归是一种自我调用函数的方式,即在函数内部的语句可以调用函数本身。
递归函数包括以下要素:1. 基本情况:递归终止的条件。
(循环体)2. 对递归函数参数的调整:递归调用时输入的参数需要满足要求。
(优化变量)递归函数的执行过程可以形象的表示为树的结构,因此也称递归函数为树的遍历。
二、递归的应用场景1. 递归遍历树型结构的数据。
2. 数学上的递归,如过程求阶乘等。
3. 递归来实现DFS深度优先搜索,BFS广度优先搜索,以及回溯算法等等。
三、递归的优点1. 递归大多数情况下可以写出比非递归更加简洁易懂的代码。
2. 可以方便地完成较为复杂的任务。
3. 隐藏了一部分数据结构,如树、栈等等结构,可以使代码更加舒适、必要。
四、递归的缺点1. 在某些情况下递归消耗更多的内存和处理时间。
2. 深度较深的递归,执行效率较低,容易导致堆栈溢出。
3. 如果递归没有正确停止,会造成死循环,程序CPU占用率过高。
五、递归实现的方法1. 斐波那契数列的递归写法:function fib(n) {if (n === 1 || n === 2) {return 1}return fib(n - 1) + fib(n - 2)}2. 阶乘的递归写法:function factorial(n) {if (n === 1) {return 1}return n * factorial(n - 1)}3. 列表反转的递归写法:function reverseList(head) {if (head == null || head.next == null) {return head}let new_head = reverseList(head.next);head.next.next = head;head.next = null;return new_head;}以上三种递归写法分别展示斐波那契数列、阶乘、列表反转的实现方法。
递归算法详细分析-> C阅读(17418)C通过运行时堆栈支持递归函数的实现。
递归函数就是直接或间接调用自身的函数。
许多教科书都把计算机阶乘和菲波那契数列用来说明递归,非常不幸我们可爱的著名的老潭老师的《C语言程序设计》一书中就是从阶乘的计算开始的函数递归。
导致读过这本经书的同学们,看到阶乘计算第一个想法就是递归。
但是在阶乘的计算里,递归并没有提供任何优越之处。
在菲波那契数列中,它的效率更是低的非常恐怖。
这里有一个简单的程序,可用于说明递归。
程序的目的是把一个整数从二进制形式转换为可打印的字符形式。
例如:给出一个值4267,我们需要依次产生字符‘4’,‘2’,‘6’,和‘7’。
就如在printf函数中使用了%d格式码,它就会执行类似处理。
我们采用的策略是把这个值反复除以10,并打印各个余数。
例如,4267除10的余数是7,但是我们不能直接打印这个余数。
我们需要打印的是机器字符集中表示数字‘7’的值。
在ASCII码中,字符‘7’的值是55,所以我们需要在余数上加上48来获得正确的字符,但是,使用字符常量而不是整型常量可以提高程序的可移植性。
‘0’的ASCII码是48,所以我们用余数加上‘0’,所以有下面的关系:‘0’+ 0 =‘0’‘0’+ 1 =‘1’‘0’+ 2 =‘2’...从这些关系中,我们很容易看出在余数上加上‘0’就可以产生对应字符的代码。
接着就打印出余数。
下一步再取商的值,4267/10等于426。
然后用这个值重复上述步骤。
这种处理方法存在的唯一问题是它产生的数字次序正好相反,它们是逆向打印的。
所以在我们的程序中使用递归来修正这个问题。
我们这个程序中的函数是递归性质的,因为它包含了一个对自身的调用。
乍一看,函数似乎永远不会终止。
当函数调用时,它将调用自身,第2次调用还将调用自身,以此类推,似乎永远调用下去。
这也是我们在刚接触递归时最想不明白的事情。
但是,事实上并不会出现这种情况。
这个程序的递归实现了某种类型的螺旋状while循环。
while循环在循环体每次执行时必须取得某种进展,逐步迫近循环终止条件。
递归函数也是如此,它在每次递归调用后必须越来越接近某种限制条件。
当递归函数符合这个限制条件时,它便不在调用自身。
在程序中,递归函数的限制条件就是变量quotient为零。
在每次递归调用之前,我们都把quotient除以10,所以每递归调用一次,它的值就越来越接近零。
当它最终变成零时,递归便告终止。
/*接受一个整型值(无符号0,把它转换为字符并打印它,前导零被删除*/#include <stdio.h>int binary_to_ascii( unsigned int value){unsigned int quotient;quotient = value / 10;if( quotient != 0)binary_to_ascii( quotient);putchar ( value % 10 + '0' );}递归是如何帮助我们以正确的顺序打印这些字符呢?下面是这个函数的工作流程。
1. 将参数值除以102. 如果quotient的值为非零,调用binary-to-ascii打印quotient当前值的各位数字3. 接着,打印步骤1中除法运算的余数注意在第2个步骤中,我们需要打印的是quotient当前值的各位数字。
我们所面临的问题和最初的问题完全相同,只是变量quotient的值变小了。
我们用刚刚编写的函数(把整数转换为各个数字字符并打印出来)来解决这个问题。
由于quotient的值越来越小,所以递归最终会终止。
一旦你理解了递归,阅读递归函数最容易的方法不是纠缠于它的执行过程,而是相信递归函数会顺利完成它的任务。
如果你的每个步骤正确无误,你的限制条件设置正确,并且每次调用之后更接近限制条件,递归函数总是能正确的完成任务。
但是,为了理解递归的工作原理,你需要追踪递归调用的执行过程,所以让我们来进行这项工作。
追踪一个递归函数的执行过程的关键是理解函数中所声明的变量是如何存储的。
当函数被调用时,它的变量的空间是创建于运行时堆栈上的。
以前调用的函数的变量扔保留在堆栈上,但他们被新函数的变量所掩盖,因此是不能被访问的。
当递归函数调用自身时,情况于是如此。
每进行一次新的调用,都将创建一批变量,他们将掩盖递归函数前一次调用所创建的变量。
当我追踪一个递归函数的执行过程时,必须把分数不同次调用的变量区分开来,以避免混淆。
程序中的函数有两个变量:参数value和局部变量quotient。
下面的一些图显示了堆栈的状态,当前可以访问的变量位于栈顶。
所有其他调用的变量饰以灰色的阴影,表示他们不能被当前正在执行的函数访问。
假定我们以4267这个值调用递归函数。
当函数刚开始执行时,堆栈的内容如下图所示:执行除法之后,堆栈的内容如下:接着,if语句判断出quotient的值非零,所以对该函数执行递归调用。
当这个函数第二次被调用之初,堆栈的内容如下:堆栈上创建了一批新的变量,隐藏了前面的那批变量,除非当前这次递归调用返回,否则他们是不能被访问的。
再次执行除法运算之后,堆栈的内容如下:quotient的值现在为42,仍然非零,所以需要继续执行递归调用,并再创建一批变量。
在执行完这次调用的出发运算之后,堆栈的内容如下:此时,quotient的值还是非零,仍然需要执行递归调用。
在执行除法运算之后,堆栈的内容如下:不算递归调用语句本身,到目前为止所执行的语句只是除法运算以及对quotient的值进行测试。
由于递归调用这些语句重复执行,所以它的效果类似循环:当quotient的值非零时,把它的值作为初始值重新开始循环。
但是,递归调用将会保存一些信息(这点与循环不同),也就好是保存在堆栈中的变量值。
这些信息很快就会变得非常重要。
现在quotient的值变成了零,递归函数便不再调用自身,而是开始打印输出。
然后函数返回,并开始销毁堆栈上的变量值。
每次调用putchar得到变量value的最后一个数字,方法是对value进行模10取余运算,其结果是一个0到9之间的整数。
把它与字符常量‘0’相加,其结果便是对应于这个数字的ASCII字符,然后把这个字符打印出来。
输出4:接着函数返回,它的变量从堆栈中销毁。
接着,递归函数的前一次调用重新继续执行,她所使用的是自己的变量,他们现在位于堆栈的顶部。
因为它的value 值是42,所以调用putchar后打印出来的数字是2。
输出42:接着递归函数的这次调用也返回,它的变量也被销毁,此时位于堆栈顶部的是递归函数再前一次调用的变量。
递归调用从这个位置继续执行,这次打印的数字是6。
在这次调用返回之前,堆栈的内容如下:输出426:现在我们已经展开了整个递归过程,并回到该函数最初的调用。
这次调用打印出数字7,也就是它的value参数除10的余数。
输出4267:然后,这个递归函数就彻底返回到其他函数调用它的地点。
如果你把打印出来的字符一个接一个排在一起,出现在打印机或屏幕上,你将看到正确的值:4267汉诺塔问题递归算法分析:一个庙里有三个柱子,第一个有64个盘子,从上往下盘子越来越大。
要求庙里的老和尚把这64个盘子全部移动到第三个柱子上。
移动的时候始终只能小盘子压着大盘子。
而且每次只能移动一个。
1、此时老和尚(后面我们叫他第一个和尚)觉得很难,所以他想:要是有一个人能把前63个盘子先移动到第二个柱子上,我再把最后一个盘子直接移动到第三个柱子,再让那个人把刚才的前63个盘子从第二个柱子上移动到第三个柱子上,我的任务就完成了,简单。
所以他找了比他年轻的和尚(后面我们叫他第二个和尚),命令:① 你丫把前63个盘子移动到第二柱子上② 然后我自己把第64个盘子移动到第三个柱子上后③ 你把前63个盘子移动到第三柱子上2、第二个和尚接了任务,也觉得很难,所以他也和第一个和尚一样想:要是有一个人能把前62个盘子先移动到第三个柱子上,我再把最后一个盘子直接移动到第二个柱子,再让那个人把刚才的前62个盘子从第三个柱子上移动到第三个柱子上,我的任务就完成了,简单。
所以他也找了比他年轻的和尚(后面我们叫他第三和尚),命令:① 你把前62个盘子移动到第三柱子上② 然后我自己把第63个盘子移动到第二个柱子上后③ 你把前62个盘子移动到第二柱子上3、第三个和尚接了任务,又把移动前61个盘子的任务依葫芦话瓢的交给了第四个和尚,等等递推下去,直到把任务交给了第64个和尚为止(估计第64个和尚很郁闷,没机会也命令下别人,因为到他这里盘子已经只有一个了)。
4、到此任务下交完成,到各司其职完成的时候了。
完成回推了:第64个和尚移动第1个盘子,把它移开,然后第63个和尚移动他给自己分配的第2个盘子。
第64个和尚再把第1个盘子移动到第2个盘子上。
到这里第64个和尚的任务完成,第63个和尚完成了第62个和尚交给他的任务的第一步。
从上面可以看出,只有第64个和尚的任务完成了,第63个和尚的任务才能完成,只有第2个和尚----第64个和尚的任务完成后,第1个和尚的任务才能完成。
这是一个典型的递归问题。
现在我们以有3个盘子来分析:第1个和尚命令:① 第2个和尚你先把第一柱子前2个盘子移动到第二柱子。
(借助第三个柱子)② 第1个和尚我自己把第一柱子最后的盘子移动到第三柱子。
③ 第2个和尚你把前2个盘子从第二柱子移动到第三柱子。
很显然,第二步很容易实现(哎,人总是自私地,把简单留给自己,困难的给别人)。
其中第一步,第2个和尚他有2个盘子,他就命令:① 第3个和尚你把第一柱子第1个盘子移动到第三柱子。
(借助第二柱子)② 第2个和尚我自己把第一柱子第2个盘子移动到第二柱子上。
③ 第3个和尚你把第1个盘子从第三柱子移动到第二柱子。
同样,第二步很容易实现,但第3个和尚他只需要移动1个盘子,所以他也不用在下派任务了。
(注意:这就是停止递归的条件,也叫边界值)第三步可以分解为,第2个和尚还是有2个盘子,命令:① 第3个和尚你把第二柱子上的第1个盘子移动到第一柱子。
② 第2个和尚我把第2个盘子从第二柱子移动到第三柱子。
③ 第3个和尚你把第一柱子上的盘子移动到第三柱子。
分析组合起来就是:1→3 1→2 3→2 借助第三个柱子移动到第二个柱子 |1→3 自私人留给自己的活| 2→1 2→3 1→3借助第一个柱子移动到第三个柱子|共需要七步。
如果是4个盘子,则第一个和尚的命令中第1步和第3步各有3个盘子,所以各需要7步,共14步,再加上第1个和尚的1步,所以4个盘子总共需要移动7+1+7=15步,同样,5个盘子需要15+1+15=31步,6个盘子需要31+1+31=64步……由此可以知道,移动n个盘子需要(2的n次方)-1步。