从"汉诺塔"经典递归到JS递归函数

前言

参考《JavaScript语言精粹》

递归是一种强大的编程技术,他把一个问题分解为一组相似的子问题,每一问题都用一个寻常解去解决。递归函数就是会直接或者间接调用自身的一种函数,一般来说,一个递归函数调用自身去解决它的子问题。

"汉诺塔"经典递归问题

"汉诺塔"是印度的一个古老传说,也是程序设计中的经典的递归问题,是一个著名的益智游戏:

  题目如下:

    塔上有三根柱子和一套直径各不相同的空心圆盘,开始时源柱子上的所有圆盘都按从大到小的顺序排列。目标是通过每一次移动一个圆盘到另一根柱子上,最终把一堆圆盘移动到目标柱子上,过程中不允许把较大的圆盘放置在较小的圆盘上;

    

寻找规律(把所有的圆盘移动到C):

  1)n(圆盘个数) == 1

    第一次:1号盘  A -> C      sum(移动次数) = 1

  2)n == 2

    第一次:1号盘 A -> B

    第二次:2号盘 A -> C

    第三次:1号盘 B -> C  sum = 3

  3)n == 3

    第一次:1号盘 A -> C

    第二次:2号盘 A -> B

    第三次:1号盘 C -> B

    第四次:3号盘 A -> C

    第五次:1号盘 B -> A

    第六次:2号盘 B -> C

    第七次:1号盘 A -> C  sum = 7

   

  故不难发现规律,移动次数为:sum = 2^n - 1 

算法分析(递归):

  把一堆圆盘从一个柱子移动另一根柱子,必要时使用辅助的柱子。可以把它分为三个子问题:

    首先,移动一对圆盘中较小的圆盘到辅助柱子上,从而露出下面较大的圆盘,

    其次,移动下面的圆盘到目标柱子上

    最后,将刚才较小的圆盘从辅助柱子上在移动到目标柱子上

   把三个步骤转化为简单数学问题:

    (1)     把 n-1个盘子由A 移到 B;

    (2)     把 第 n个盘子由 A移到 C;

    (3)     把n-1个盘子由B 移到 C;

  我们创建一个JS函数,当它调用自身的时候,它去处理当前正在处理圆盘之上的圆盘。最后它回一个不存在圆盘去调用,在这种情况下,它不在执行任何操作。

JavaScript源代码实现

var hanoi = function(disc,src,aux,dst){

    if(disc>0){

        hanoi(disc-1,src,dst,aux);

        console.log(‘ 移动 ‘+ disc +  ‘ 号圆盘 ‘ + ‘ 从 ‘ + src +  ‘ 移动到 ‘ +  dst);

        hanoi(disc-1,aux,src,dst)

    }

}

hanoi(3,‘A‘,‘B‘,‘C‘)

整个算法的思路是:

  1. 将A柱子上的n-1个盘子暂时移到B柱子上
  2. A柱子只剩下最大的盘子,把它移到目标柱子C上
  3. 最后再将B柱子上的n-1

JS递归函数遍历Dom

  递归函数可以非常高效的操作D树形结构,在JavaScript有一种"天然的树形结构"浏览器端的文档对象模型(Dom)。每次递归调用时处理指定树的一小段。

/*      我们定义一个walk_the_DOM函数,
  1) 它从某个指定的节点开始,按指定HTML源码的顺序,访问树的每个节点
   2)它会调用一个函数,并依次传递每个节点给它,walk_the_DOM调用自身去处理每一个节点
*/
var walk_the_DOM = function walk( node , func ) {

    func(node);

    node = node.firstChild;

    while (node) {

        walk( node , func );

        node = node.nextSibling;

     }    

}

/*    在定义一个getElementByAttribute函数
1) 它以一个属性名称字符串和一个可选的匹配值作为参数
2) 它调用walk_the_DOM,传递一个用来查找节点属性名的函数作为参数,匹配得节点都会累加到一个数组中
*/
              var getElementsByAttribute=function(att,value){
                var results=[];

                walk_the_DOM(document.body,function(node){

                    var actual=node.nodeType===1&&node.getAttribute(att);

                    if(typeof actual===‘string‘ &&( actual===value|| typeof value!==‘string‘)){

                        results.push(node);

                    }
                });
                return results;
            }

命名函数表达式和递归

递归问题

求阶乘的函数:

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*factorial(num-1);
    }
}

通过将函数factorial设置为null,使原始函数的引用只剩一个, 此时factorial已不再是函数

arguments.callee实现递归

arguments.callee是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*arguments.callee(num-1);
    }
}
var anotherFactorial=factorial;
factorial=null;
anotherFactorial(3) //6

用arguments.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此,在编写递归函数时,使用arguments.callee总比使用函数名更保险。

但是在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会报错

命名函数表达式实现递归

创建一个名为f()的命名函数表达式,然后赋值给factorial,即使把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正常完成。

这种方式在严格模式和非严格模式都可行。

var factorial =function f(num){
    ‘use strict‘
    if(num<=1){
        return 1;
    }else{
        return num* f (num-1);
    }
}

factorial(3)    //6
var anotherFactorial=factorial;
factorial=null;
anotherFactorial(3)      //6
时间: 2024-12-05 21:35:23

从"汉诺塔"经典递归到JS递归函数的相关文章

汉诺塔问题递归算法分析

汉诺塔问题递归算法分析: 一个庙里有三个柱子,第一个有64个盘子,从上往下盘子越来越大.要求庙里的老和尚把这64个盘子全部移动到第三个柱子上.移动的时候始终只能小盘子压着大盘子.而且每次只能移动一个. 1.此时老和尚(后面我们叫他第一个和尚)觉得很难,所以他想:要是有一个人能把前63个盘子先移动到第二个柱子上,我再把最后一个盘子直接移动到第三个柱子,再让那个人把刚才的前63个盘子从第二个柱子上移动到第三个柱子上,我的任务就完成了,简单.所以他找了比他年轻的和尚(后面我们叫他第二个和尚),命令:

【C/C++学院】0817-递归汉诺塔 双层递归 /CPP结构体 /面向过程与面向对象的编程模式/类的常识共用体实现一个类的特征/QT应用于类以及类的常识

递归汉诺塔 双层递归 #include <iostream> void han(int n, char A, char B, char C) { static int num = 1; std::cout << "第" << num << "次"; num++; if (n<1) { return; } else { han(n - 1, A, C, B); std::cout << A <&l

用C语言实现汉诺塔自动递归演示程序

用C语言实现汉诺塔自动递归演示程序 程序实现效果 1.变界面大小依照输入递归数改变. 2.汉诺塔自动移动演示. 3.采用gotoxy实现流畅刷新. 4.保留文字显示递归流程 程序展示及实现 github地址:https://github.com/404name/C-game 0.主体思路 输入要递归的汉诺塔数目,在原来的汉诺塔基础上新增move_play函数展示递归,用next数组存储每种移动状态.对应的从哪到哪可自动对应相应的移动方式自动移动. 1.变界面大小依照输入递归数改变 init函数按

函数递归——汉诺塔经典题型

理解汉诺塔游戏规则,有A,B,C座塔,将A塔上的圆盘移动到C塔上,当A塔只有一块圆盘时,直接移动到C塔,当A塔有N个圆盘时,需要将N-1个圆盘移动到B塔,然后将剩下的最底下圆盘移动到C.大盘不能压住小盘 汉诺塔游戏主要考虑到最底下圆盘的调用,每次的移动都假设到最底下圆盘. 其实到目前我还是不太明白 def move(n, x, y, z): if n==1: print (x,'-->',z) return move(n-1,x,z,y)#将前n-1个盘子从x移动到y上 move(1,x,y,z

HDU 2064 汉诺塔III(递归)

题目链接 Problem Description 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到大顺序串着由64个圆盘构成的塔.目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面.现在我们改变游戏的玩法,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到下盘的上面.Daisy已经做过原来的汉诺塔问题和汉诺塔II,但碰到这个问题时,她想了很久都不能解决,现在

汉诺塔之递归学习

汉诺塔问题: 问题描述引自:http://www.cnblogs.com/antineutrino/p/3334540.html 汉诺塔问题是一个经典的问题.汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规定,任何时候,在小圆盘上都不能放大圆盘,且在三根柱子之间一次只能移动一个圆盘. 思维导图: 程序代码: 1 #

Hanio汉诺塔代码递归实现

1.背景介绍 Hanio (汉诺塔,又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘. 我们姑且不去追溯传说的缘由,现考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序.这需要多少次移动呢?这里需要递归的方法.假设有n片,移动次数是f(n).显然f

[算法]——汉诺塔的递归深度

今天早晨在上班的路上,一好朋友突然提到之前的一个计算机的考题,汉诺塔(相信大家都玩过)的递归深度. 由于很久没有看算法,以及脑容量有限,当时没有多想. 来到公司后,把公式列了一下,终于清晰多了. 下面假设3根柱子编号为1,2,3. 主要思路: 把n个圆盘从3号移到1号 = 把n-1个圆盘从3号移到2号 + 把第n个圆盘从3号移到1号 + n-1个圆盘从2号移到1号 列出公式: f(n) = f(n-1) + 1 + f(n-1) = 2f(n-1) + 1 计算公式: 接下来就是数学题了, 利用

【Python实践-3】汉诺塔问题递归求解

1 # -*- coding: utf-8 -*- 2 #汉诺塔移动问题 3 # 定义move(n,a,b,c)函数,接受参数n,表示3个柱子A.B.C中第1个柱子A的盘子数量 4 # 然后打印出把所有盘子从A借助B移动到C的方法 5 def move(n,a,b,c): 6 if n==1: 7 print('move', a, '-->', c) 8 else: 9 move(n-1,a,c,b) 10 move(1,a,b,c) 11 move(n-1,b,a,c) 12 move(5,'