zoukankan      html  css  js  c++  java
  • 三个故事学递归

    汉诺塔

    大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在
    另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘.

    汉诺塔
    百度百科上关于这个问题,提到了递归,但得出结论其实却是用归纳的方法,通过总结:一片需要1步,两片需要3步,三片需要7步,最终总结出2^n-1的规律。那么用递归的思想该怎么思考呢?

    假设有5片圆盘,按一般方法来思考,就是按步就班移动,先移上边最小的圆盘移到B柱子上,然后将第二个圆盘移到C柱子上。。。。。。但是这个不是递归的思考方法,递归重要的一点是找出问题的层次找到递归的终点。按递归的方式是这么来思考的:

    * 移动前四片圆片到B柱子  1,2,3,4 => B
    * 移动最底下圆片到C柱子  5 => C
    * 移动前四片圆片到C柱子  1,2,3,4 => C
    
    

    那么剩下的四片具体是怎么移动的?这不是耍流氓吗?其实移动四片就像阉割版的移动5片,嗯,对,阉割了一片。就像下面这样:

    * 移动前三片圆片到C柱子 1,2,3 => C
    * 移动4圆片到B柱子      4 => B
    * 移动前三片圆片到B柱子 1,2,3 => B
    

    就这样,这就是递归的方式。一个大问题分解成几步和一个与原问题相似的小问题,然后把上面的翻译成函数,就像下面这样:

    function steps(n){//n表示几片圆片,steps表示需要的总步数
        if (n === 1){
            return 1;
        }
        return steps(n-1) + 1 + steps(n-1);
    }
    

    上面只是算出了步骤,更详细一点,我们还可以把每一步操作用console.log打印出来:

    
    // 汉诺塔
    // 假设柱子分别叫a,b,c
    /**
     * 
     * @param {Number} index 几个圆盘
     * @param {String} from 从哪个柱子开始
     * @param {String} to 到哪个柱子结束
     */
    function move(index, from, to){
      const sticks = ['a','b','c'];
      let other //除from,to外第三个柱子的名称
      for(let s of sticks){
        if (s !== from && s !== to){
         other = s
         break;
        }
      }
      if(index === 1){
        return console.log(`将${index}从${from}移到${to}`)
      }
      
      move(index-1, from, other);
      console.log(`将${index}从${from}移到${to}`);
      move(index-1, other, to);
    }
    //将3个圆片从a移到b
    move(3, 'a', 'b')
    

    上楼梯

    还有一个比较有意思的是上楼梯问题,可以看成是波菲那切问题的另一个描述:

    假设你正在爬楼梯,楼梯一共有n级。但每次你只能爬一步或者两步或者三步,你能有多少种不同的方法爬到楼顶部?

    假如我们从底部开始思考,那么很快就乱掉了,反正我是这样。那么现在是时候展示递归的威力了。前面说过,递归的重点在于将一个大问题分成几步和一个和大问题相似的小问题。所以你要倒过来思考。假设你已经到顶楼了,那最后一次可能是迈了一步,两步,或者三步,(一步一步似爪牙,似魔鬼的步伐,似魔鬼的步伐)。
    于是步骤就有了:

    * 走到倒数第三级 + 走三步
    * 走到倒数第二级 + 走二步
    * 走到倒数第一级 + 走一步
    

    至于是怎么走到倒数第三级,倒数第二级,倒数第一级,交给递归。

    function ways(n){
        if(n === 1){
            return 1;
        }
        if(n === 2){
            return 2;
        }
        if(n === 3){
            return 4;
        }
        return ways(n-1) + ways(n-2) + ways(n-3)
    }
    

    不要在控制台里输ways(100)这样的调用,人会累死,浏览器会挂掉。

    站队形

    其实这个故事是我编的,为了描述快速排序算法用的.

    假如小明,小明和小明和他们的同学们在教室门口,老师让他们从低到高排成一排,一个小明站到第一个,一个小明站到最后一个,其他人乱哄哄的,站了半天也没好。于是老师想了一个办法:
    老师随便找了一个学生A,叫比A低的同学们站前面,比A高的同学们站后面,然后从比A低的同学们找了一个同学C,让这波同学比C高的站后边,比C低的站C前边。。。。于是队很快就排好了。

    把上面的过程描述一下就是:

    * 找一个基准
    * 找出比基准大和比基准小或等于基准的两组数
    * 对这两组数重复上面的操作
    

    假设每次把第一个数作为基准:

    function quickSort(arr){
        if(arr.length <= 1 ){
            return arr;
        }
        var base = arr[0];
        var other = arr.slice(1);
        var smaller = [], bigger = [];
        for(var i = 0,len = other.length; i < len; i++ ){
            other[i] > base ? bigger.push(other[i]) : smaller.push(other[i]);   
        }
        return quickSort(smaller).concat([base]).concat(quickSort(bigger));//把排好顺序的array连起来
    
    }
    

    给递归设置正确的终点是很重要的,循环没有终点会造成死循环,递归没有终点也会造成无限递归。比如上面的条件判断。if(arr.length <= 1)是因为,一直递归下去,smaller和bigger这两个数组会是一个空数组[]或者是一个只有一个元素的数组,如果把条件改成if(arr.length === 1),代码就会有一定的几率出bug,

    * quickSort([5,3,9,2,8])//[2, 3, 5, 8, 9]
    * quickSort([5,3,8,3,8])//Maximum call stack size exceeded
    
    

    前者到递归到最后没有出现空数组,所以返回正确的值,后者出现空数组,程序却不能停止,结果报错。其实递归还有很多东西可以说,比如递归和迭代,尾递归的优化,递归和分形等等,本文就当是入门了

    我能看到一个元素吗?

    这里不考虑opacity将元素隐藏的情况,只考虑display:none的情况.

    大家都知道,判断一个元素能不能被看到,不止要判断本元素,还要判断元素的父元素是否被隐藏,这时可以用递归,不是必须的

    function canIsee(el) {
        if (el.nodeType === 'BODY') {
            return true;
        }
        if (getComputedStyle(el).display === 'none') {
            return false;
        }
        return canISee(el.parentElement);
    }
    
    
  • 相关阅读:
    Excel 常用函数
    Excel中使用VBA访问Access数据库
    Python 日期和时间戳的转换
    tensorflow中命名空间、变量命名的问题
    Git:一个简单示例
    Pyhon中运算符的使用
    python中模块包的离线下载教程
    python中的进制转换
    python几种常见的模块安装方法
    python中的模块调用
  • 原文地址:https://www.cnblogs.com/imgss/p/6850949.html
Copyright © 2011-2022 走看看