对于递归算法和二分查找的理解

前言

本人涉及web开发领域的学习以来,很少去学习算法相关知识,今日对于基础的算法进行了初步探究,包括递归、二分查找等,有所感发,发表这篇博客供大家学习参考,相互交流!

1.递归算法

例题1:递归求斐波那契数列

const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
/* 输入要求第几位 */
rl.on("line", function (line) {
  let num = Number(line);
  const test = (a) => {
    if (a === 1 || a === 2) return 1;
    return test(a - 1) + test(a - 2);
  };
  console.log(test(num));
});

递归的思想:

1.找出一个通用的解法模板,不断地去调用这个模板(自己调用自己)

2.这个模板在不断地被调用的过程中并不会得到解决,但是随着不能被解决的问题不断地堆积在栈内,总有一个点会找到一个准确的可以解决问题的值,然后会迅速向回解决所有未解决的问题

注意点:

1.递归的思想非常抽象,基本是无法通过看代码一眼看出最终运算结果的,要有抽象的思维

2.只要将一个通用的模板写好,那么接下来的递归调用就会自发进行,最后得到一个合理的结果,可以说是“山重水复疑无路,柳暗花明又一村”!

3.递归的方法中只写模板,并且这个模板是在宏观意义上直接“可以”输出结果的,只不过是抽象的“可以”。

比如n的阶乘,可以分解成n + n-1的阶乘,这种整体拆分的思想是要有的!

再比如斐波那契数列每一项就是前两项的和,那么我们递归的模板就是前两项的和

4.递归算法里面必须要有if(){return },这既是我们程序找到的那个突破点,也是保证不会栈溢出的必要操作

5.模拟递归场景要先取少数量的内容(小的数字),分析一下场景进行的过程,找到模板

6.看递归过程的时候,要每递归一次将问题数据集-1来看,这样问题会被一层一层拨开

例题2:汉诺塔问题

汉诺塔问题实际上步骤中包含了两个递归,稍微复杂一些!

/* 汉诺塔问题:汉诺塔是一个经典的递归问题。它包括三根柱子和一些大小不等的圆盘。
初始时,所有圆盘都叠在第一根柱子上,按照从大到小的顺序排列。
目标是将所有圆盘移动到第三根柱子上,并保持原有顺序。每次移动只能移动一个圆盘,并且不能将大圆盘放在小圆盘上面。
 */
const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
/* 输入盘子数量 */
rl.on("line", function (line) {
  let num = Number(line);
  const han = (n, a, b, c) => {
    if (n === 0) console.log(a + "===>" + c); //程序突破口
    else {
      //下面这三步在宏观意义上就可以得到我们想要的结果
      han(n - 1, a, c, b); //将第一个移动到第二个(实际上还是重复这个模板)
      console.log(a + "===>" + c); //将第一个的最后一个盘子直接放到第三个
      han(n - 1, b, a, c);//将第二个移动到第三个(实际上还是重复这个模板)
    }
  };
  han(num, "A", "B", "C");
});

解题思路:

我们想象只有两个盘子,那么这个模板过程就出来了:将第一个柱子上除了最后一个盘子(n-1个)都移动到第二个柱子,将第一个柱子的最后一个盘子(1个)移动到第三个柱子,将第二个柱子的所有盘子(n-1个)移动到第三个柱子

2.二分查找法

b站对应视频:二分查找为什么总是写错?_哔哩哔哩_bilibili

例题:输出一个数组里面等于5的数的第一个索引

let arr = [1, 2, 5, 5, 7];
const test = (array) => {
  //   console.log(array);
  let l = -1;
  let r = array.length;
  while (l + 1 !== r) {
    let num = parseInt(l + r);
    if (array[num] < 5) {
      l = num;
    } else {
      r = num;
    }
  }
  if (array[r] === 5) { //特判,判断是否确实是5
    return r;
  } else {
    return -1; //不存在5
  }
};
console.log(test(arr));

以上使用的是二分查找的模板:(伪代码)

l = -1, r = N
while l + 1 != N
    m = l + r / 2 (向下取整)
    if isBlue(m)
        l = m
    else
        r = m
return l or r

二分查找的思想:

1.找到isBlue的条件,即临界点(红蓝分界线)

2.输出l还是r,即分界线的左侧还是右侧

策略:

蓝色的最右边第一个值必须满足isBlue的条件(条件一般只用小于号),可以在草稿上模拟几个数字,模拟出红蓝分界状况,以此来决定输出l还是r

例如:输出数组中最先匹配到的5的索引,那么条件可以为<5,那么就取r

输出数组中最后匹配到的5的索引,那么可以取<=5,那么就取l

3.对于递归和二分的思考

递归和二分中都对于灰色部分(无法解决的问题)的思想有涉及

1.递归是把灰色部分先扩大,把问题堆栈起来,找到了解决问题的点再将灰色部分逐渐减少

2.二分是一半一半地,逐渐缩小灰色部分,直到解决问题

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼骨编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值