如何写递归( 四 )


将上述过程转换为「由上到下」尾递归代码实现(你可以尝试「由下到上」实现,可以套入范式进行验证):
public int multiply2(int A, int B) {return (A < B) ? multiply2Help(A, B, 0) : multiply2Help(B, A, 0); // 寻找最小循序次数}// missPart 为奇数除以 2 时丢失的部分public int multiply2Help(int A, int B, int missPart) {if (A < 2) return missPart + B;// 最终结果 = 丢失的部分 + 最终 B 的结果missPart += (A & 1) == 1 ? B : 0; // 是否为奇数,奇数时记录丢失的部分return multiply2Help(A >> 1, B << 1, missPart); // 位移运算优化}
4. 案例实战-青蛙跳台阶
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级台阶 。求该青蛙跳上一个 n 级的台阶总共有多少种跳法 。n = 0 时忽略 。
寻找基本情况:剩余一个台阶时只能有一种跳法 f(1) = 1f(1)=1,剩余两个台阶时只能有两种跳法 f(2) = 2f(2)=2,或者剩余 3 个台阶时有 3 种跳法(1-1-1、1-2、2-1)
寻找递推关系:如果跳 1 级台阶就少一个,结果为 f(n-1)f(n?1) 种,如果跳 2 级台阶就少 2 个,结果为 f(n-2)f(n?2) 种 。
所以推导递推关系为 f(n) = f(n-1) + f(n-2)f(n)=f(n?1)+f(n?2)
优化递归:考虑重复计算问题,因为递推关系中 n 涉及减法运算,肯定会出现重复代入 f(n)f(n) 计算,因此考虑使用数据结构保存计算过的结果 。分析数据溢出问题,如果没有给定约束条件,要考虑返回跳法是否会溢出 。
由下到上的范式套入实现:
* - 寻找递推关系 f(n)=f(n-1)+f(n-2)* - 寻找递归基本情况,跳出时返回基本情况的结果 f(1) = 1,f(2) = 2* - 修改递归函数的参数,递归调用 -> 套入递推关系,当前 n 台阶跳法为 count=f(n-1)+f(n-2)* - 使用递归函数的返回值进行计算并返回最终结果 -> 递归返回跳法数 count 即为最终结果 */public int numWays1(int n) {if (n == 1) return 1;if (n == 2) return 2;int count = numWays1(n - 1) + numWays1(n - 2);return count;}// 优化上述初步完成的递归思路:// 2 个 if 可有优化 为 if (n <= 2) return n; 减少执行次数// 递归函数重复计算问题,使用临时变量保存private final Map statusRecord = new HashMap<>();public int numWays(int n) {if (n <= 2) return n; // if 判断比计算状态判断开销小,因此先进行 iffinal Integer integer = statusRecord.get(n); // 计算状态判断,已经计算直接返回if (integer != null) return integer;int count = 0; // 最终结果int count1 = numWays(n - 1); // 返回中间变量int count2 = numWays(n - 1); // 返回中间变量int count = count1 + count2; // 中间变量计算结果为最终结果statusRecord.put(n, count); // 计算的结果保存至状态表return count;}// 至此除了数据溢出问题没有处理,重复计算已优化 。
由上到下的范式套入实现:
/*** 由上到下的范式套入实现:* * - 寻找递推关系* 由下到上递推关系为,f(n) = f(n-1) + f(n-2) 相当于从 n-1 的计算过程,先从 n 找到 1,然后在从 1 累加到 n 的过程* 我们改为从 1-n 的过程,f(i+1) = f(i) + f(i-1),i+1==n 时计算结束,累加的过程变量需要我们提取为中间变量参数* * - 创建新函数,将「由下到上-范式」中的最终结果计算依赖的中间变量提取为函数的参数* 将 f(i),f(i-1) 的变量保存,初始调用我们使用 f(2) = f(1) + f(0) = 1 + 1 作为初始状态* * - 寻找基本情况,跳出时返回基本情况的结果与中间变量的计算结果(最终结果)-> if (i >= n) return a + b;* * - 根据函数参数与中间变量重新计算出新的中间变量* f(i) = f(i-1) + f(i-2) = a + b* f(i+1) = f(i) + f(i-1) = (a+b) + b* * - 修改参数 -> i + 1 递进一步* * - 递归调用并返回(该处的返回由基本情况条件触发)*/public int numWaysTail(int n) {if (n < 2) return n;return numWaysTailHelp(n, 2, 1, 1);}private int numWaysTailHelp(int n, int i, int a, int b) {if (i >= n) return a + b;return numWaysTailHelp(n, i + 1, a + b, a);}// 因为是从 1-n 的计算,所以不会出现重复计算过程 。