递归与回溯 JS 版

457

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

递归之所以叫做递归,因为可以分成递去归来两部分,按我个人的理解,递去就是从上往下,从这层递交到下一层去处理,而归来就是从下往上,也就是我下层的问题解决了,在返回到上层的位置。可以比较抽象,所以在解决具体问题前,我们先介绍一下递归思想。

递归的三个要素

递归的基本性质就是函数调用,在处理问题的时候,递归往往是把一个大规模的问题不断地变小然后进行推导的过程。在解决这类问题的时候,我们首先需要思考的有三个要素:

第一要素:明确递归终止条件

递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来。换句话说,该临界点就是一种简单情境,可以防止无限递归。

第二要素:给出递归终止时的处理办法

在到达递归的临界点下,我们应该直接给出问题的解决方案。一般地,在这种情境下,问题的解决方案是直观的、容易的。

第二要素:提取重复的逻辑,缩小问题规模。

递归的内涵就是化繁为简,把大问题缩小成若干小问题,这些小问题可以用相同的解决思路来解决。从程序上讲就是需要抽象出一个简单的直观的可重复的逻辑,以便使用相同的方法解决字问题。

递归的内涵

递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。

解决递归问题的关键就是找出这个子问题。

编程模型

在了解了递归的要素和内涵后,就可以编写具体的算法,递归一般有两种典型的模型。
模型一: 在递去的过程中解决问题

function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
        solve;                // 递去
        recursion(小规模);     // 递到最深处后,不断地归来
    }
}

模型二: 在归来的过程中解决问题

function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
        recursion(小规模);     // 递去
        solve;                // 归来
    }
}

实际问题

案例1:斐波那契数列

斐波那契数列的是这样一个数列:1、1、2、3、5、8、13、21、34....,即第一项 f(1) = 1,第二项 f(2) = 1.....,第 n 项目为 f(n) = f(n-1) + f(n-2)。求第 n 项的值是多少。

找出递归结束的条件:显然,当 n = 1 或者 n = 2 ,我们可以轻易着知道结果 f(1) = f(2) = 1。所以递归结束条件可以为 n <= 2。代码如下:

function f(n){
    if(n <= 2){
        return 1;
    }
}

然后思考,将大问题转化成若干小问题,计算f(n) 先要去计算 n-1 和 n-2 的斐波那契数列,这个两个就是可重复的小问题。所以代码可以这么写

function f(n){
    if(n <= 2){
        return 1;
    }
    return f(n-1) + f(n - 2);
}

这样就OK了,是不是挺简单的。

杨辉三角的取值

获取杨辉三角指定行、列(从0开始)的值 * 注意:与是否创建杨辉三角无关

杨辉三角形又称Pascal三角形,它的第i+1行是(a+b)i的展开式的系数。 它的一个重要性质是:三角形中的每个数字等于它两肩上的数字相加。

function getValue(x, y) {
    if(y <= x && y >= 0){
        if(y == 0 || x == y){   // 递归终止条件
            return 1; 
        }else{ 
            // 递归调用,缩小问题的规模
            return getValue(x-1, y-1) + getValue(x-1, y); 
        }
    }
    return -1;
} 

计算 x 的 n 次幂函数(即,x^n)

这道题我们需要模仿运算方法 Math.pow

第一步就是找出终止条件,我们都知道,任何数的0次幂都为1,所以n为0时就是终止条件。然后再把问题分解成若干小问题,因为我们的终止条件是n===0,所以递归时我们的目的就是把n最终化解成 1,题目中的n是可以为负数的,所以 n < 0 时需要单独处理。
那么要怎么把 n的值变小呢,如果n是偶数的话x^n我们可以这么写(x*x)^(n/2)这样就可以把n缩小了
如果是奇数可以化成偶数x^n = x * x^(n-1),这样我们就把大问题化解成若干小问题。
最终代码如下

var myPow = function(x, n) {
  
  if(n === 0) return 1;
  if(n<0) return 1/myPow(x, -n)
  
  if(n % 2) {
    return x * myPow(x, n-1)
  }
  return myPow(x*x, n/2)
};