javascript 基础之 - 递归

举个例子, 一组小朋友排了一个纵队, 类似这样
‘张三’ -> ‘李四’ -> ‘王五’ -> ‘小明’ -> ‘小花’ -> ‘小黑’ ;
假如队列报数, 从 张三 开始, 很容易就能得出队伍的长度, 这是一个累加的过程.
现在又假如, 直接问 小黑 , 现在队伍里有多少人, 当然可以从 小黑 处报数再统计.
但还有一种方法就是:
小黑 问他前面的人 小花 : ” 嘿, 哥们, 你前头有多少人啦?” ,
小花 也不清楚, 只能继续问 小明 : ” 嘿, 哥们, 你前头有多少人啊? “,
直到 李四张三 : : ” 嘿, 哥们, 你前头有多少人啊? ”
张三 告诉 李四 : “我前面 0 个人” ,
李四 再告诉 王五 : “我前面0+1 个人”,
王五 再告诉 小明 : ‘我前面1+1个人” ,
小明 再告诉 小花 : ‘我前面2+1个人” ,
小花 再告诉 小黑 : ‘我前面3+1个人” ,
小黑 掐指一算 , 我前面就是 4 + 1 个人. 再加上我自己, 就是总共6个人啦.

概念

大问题的解, 可以通过 n 次 求解得出结论. 大问题的解依赖小问题的解, 小问题的解又依赖小小问题的解, 以此类推 , 每一次缩小问题, 都需要等待再小问题的解决 , 直到一个问题足够小, 有确定解返回.

递归的几大要素
  • 可期的终止条件 , 比如 张三 前面没有人为0
  • 相同的求解方法 , 比如 都是 相同的询问 , ” 嘿, 哥们, 你前头有多少人啊? “
  • 小问题的解, 通过 相同的解法, 得到大问题的解. 比如 0+1 , 1+1 , 都是前面的人返回 + 1
抽象成递归基本套路
  • 分析

    • 知道什么, 即终止条件?
    • 可以知道什么?
    • 求什么?
    • 找到缩减问题的准则
    • 缩减前后两者的关系
    • 抽象通用方法
  • 代码

    • 先写一个函数 ( a )
    • if( 终止条件 ) { return 终止结果}
    • 函数 ( a ) 体内 return 这个函数 ( a )
    • return 这个函数 ( a ) 时根据 递减问题准则和前后两者关系 做调整, 函数的参数为递减的问题的指标, 比如 天, 比如数字递减.
示例

- 求 n 阶乘 ?

缩减问题的准则: 减 1
缩减前后两者的关系: n! = n* (n-1)!
抽象通用方法: fact(n) = n* fact(n-1)
找到终止条件: fact(1) = 1

// step1 先写一个函数
function fact(){}

// step2  if( 终止条件 ) { return 终止结果}
function fact(n){
    if(n===1){
        return 1;
    }
}

//step3   函数 ( a ) 体内 return 这个函数 ( a )
function fact(n){
    if(n===1){
        return 1;
    }
    return fact();
}

// step4  return 这个函数 ( a ) 时根据 递减问题准则和前后两者关系 做调整
function fact(n){
    if(n===1){
        return 1;
    }
    return n*fact(n-1);
}

- 一个猴子摘了n个桃子, 第一天吃了 n/2 +1 个桃 , 第二天吃了上一天的 n/2 + 1 个桃 , 直到最后一天 ( 第9天 ) 吃到剩下1个桃. 那么他总共摘了多少个桃 ?

分析

  • 知道什么? 终止条件 => 最后一天剩 1 个桃
  • 可以知道什么? 倒数第二天开始有多少个桃 => x - (x/2+1) = 1 , 倒数第二天共 (1+1) *2 个 , 括号中 的 第一个 1 为后面一天的初始个数 , 第二个 1 为常数 ;
  • 求什么? 第一天有多少个桃.
  • 缩减规则 => 天递减
  • 前后者关系 => 前面一天的桃 等于 (今天的桃+1) * 2
  • 终止条件 => 最后一天 1 个桃
// step1 先写一个函数
function getPeachNum(day) {

}

// step2 if 终止
function getPeachNum(day) {
    if(day===9){
        return 1;
    }
}

// step3 函数体内返回这个函数
function getPeachNum(day) {
    if(day===9){
        return 1;
    }
    return getPeachNum(day);
}

// return 这个函数 ( a ) 时根据 递减问题准则和前后两者关系 做调整 
// 前面一天的桃 等于 (今天的桃+1) * 2
function getPeachNum(day) {
    if(day===9){
        return 1;
    }
    return (getPeachNum(day+1)+1)*2;
}
getPeachNum(1); // 得第一天的桃子 共 766 个
  • 树形结构

这里结束的条件 , 是循环的结束,即结束.

// 原数据
var originData = [
  { title: '标题1', id: '1', pid: '0' },
  { title: '标题1-1', id: '1-1', pid: '1' },
  { title: '标题1-2', id: '1-2', pid: '1' },
  { title: '标题2', id: '2', pid: '0' },
  { title: '标题2-1', id: '2-1', pid: '2' },
  { title: '标题2-2', id: '2-2', pid: '2' },
  { title: '标题2-1-1', id: '2-1-1', pid: '2-1' },
  { title: '标题2-2-1', id: '2-2-1', pid: '2-2' },
  { title: '标题2-2-2', id: '2-2-2', pid: '2-2' },
  { title: '标题2-2-2-1', id: '2-2-2-1', pid: '2-2-2' },
  { title: '标题2-2-2-2', id: '2-2-2-2', pid: '2-2-2' },
];

// 转成树形结构
function toTree(arr, pid,linkKey='children') {
  var treeArr = [];
  var allTreeLeaf = arr.filter(item => item.pid === pid);
  allTreeLeaf.forEach(tree => {
    let _children = toTree(arr, tree.id,linkKey)
    if (_children.length) {
      tree[linkKey]= _children;
    }
    treeArr.push(tree);
  })
  return treeArr;
}

var formatTree = toTree(originData,'0');

// 将树形结构展平还原回去
function flattenTree(treeData, linkKey) {
  var result = [];
  treeData.forEach(thrunk => {
    if (thrunk[linkKey] && thrunk[linkKey].length) {
      result = result.concat(flattenTree(thrunk.children, linkKey))
    }
    delete thrunk[linkKey]
    result = result.concat(thrunk);
  })
  return result;
}

console.log(flattenTree(formatTree, 'children'))

猜你喜欢

转载自blog.csdn.net/haokur/article/details/80498145
今日推荐