递归算法(及其衍生算法:缓存,分治,回溯)

news2024/12/23 4:30:38

文章目录

  • 一、初识递归
  • 二、缓存
  • 三、分治
  • 四、回溯

一、初识递归

递归函数 = 终止条件 + 递归关系
终止条件: 当大问题被拆解成能轻松解决的小问题时,运行终止条件中的逻辑
递归关系: 定义如何将大问题拆解为小问题

例子:小名跑步。
例如:小名跑4公里,可以分为(跑1km+再跑3km)-> (跑1km+再跑2km)-> (跑1km+再跑1km)-> (跑完全程)
实现:

public void running(int distance) {
    if (distance == 0) { // 终止条件
        System.out.println("小名跑完了全程!");
        return;
    } else {
        System.out.println("小名跑了1km");
        distance = distance - 1;
        System.out.println("还剩" + distance + "km");
        running(distance); // 递归调用
    }
}

@Test
public void test1() {
    int distance = 4;
    System.out.println("跑步总程:" + distance + "km");
    running(distance);
}

输出:

跑步总程:4km
小名跑了1km
还剩3km
小名跑了1km
还剩2km
小名跑了1km
还剩1km
小名跑了1km
还剩0km
小名跑完了全程!

正如:LeetCode: 700. 二叉搜索树中的搜索
树对象:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {
    }

    TreeNode(int val) {
        this.val = val;
    }

    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "val=" + val +
                ", left=" + left +
                ", right=" + right +
                '}';
    }
}

主要方法:

public TreeNode searchBST(TreeNode root, int val) {
    // 终止条件
    if (root == null) return null; // 搜索完所有节点,目标节点不存在
    if (root.val == val) return root; // 当前节点即为目标节点
    // 递归(已知:二叉搜索树(BST)右子树节点值大于左子树节点值)
    if (val > root.val) return searchBST(root.right, val); // 目标值大于当前节点,开始搜索右子树
    else return searchBST(root.left, val); // 目标值大于当前节点,开始搜索左子树
}

测试:

@Test
public void test() {
    TreeNode treeNode1 = new TreeNode(1);
    TreeNode treeNode3 = new TreeNode(3);
    TreeNode treeNode7 = new TreeNode(7);
    TreeNode treeNode2 = new TreeNode(2,treeNode1,treeNode3);
    TreeNode treeNode4 = new TreeNode(4,treeNode2,treeNode7);
    TreeNode treeNode = searchBST(treeNode4, 2);
    System.out.println(treeNode == null ? null : treeNode.toString());
}

输出:

TreeNode{val=2, left=TreeNode{val=1, left=null, right=null}, right=TreeNode{val=3, left=null, right=null}}

递归三种形式:
1.Memorization缓存:将计算结果保存,避免重复计算
2.Divide and conquer分治:将一个大问题分解成小问题,各个击破,然后将“小问题的解”组合起来
3.Backracking回溯:逐步尝试所有满足条件的结果,一旦发现不可行的解,立即停止。

二、缓存

  1. 初始化缓存
  2. 如果缓存中存在答案,则直接返回
  3. 将计算结果写入缓存

正如:LeetCode:509. 斐波那契数
在这里插入图片描述
题目提示中提到:
f(0) = 0,f(1) = 1
f(n) = f(n - 1) + f(n - 2),其中 n > 1
所以我不难计算出f(2)=1,从上图我们可以看出f(2)被计算了两次,所以这里我们用缓存来减少加法的次数。

public int fib(int n) {
    //1.初始化缓存
    int[] memo = new int[n+1];
    int res = helper(memo, n);
    return res;
}

public int helper(int[] memo, int n){
    if (n < 2) {
        return n;
    }
    //2.如果缓存中存在答案,则直接返回
    if(memo[n]!=0){
        return memo[n];
    }
    //3.将计算结果写入缓存
    memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
    return memo[n];
}

测试:

@Test
public void test3(){
    int fib = fib(4);
    System.out.println(fib);
}

输出:

3

三、分治

  1. 把大问题分为一系列小问题
  2. 递归求解每个子问题
  3. 合并每个子问题的结果
    二叉搜索树(BST):左子树的所有值要比根节点小;右子树的所有值要比根节点大

正如:LeetCode:98. 验证二叉搜索树

public boolean isValidBST(TreeNode root) {
    return helper(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

public boolean helper(TreeNode node, long lower, long upper){
    if (node == null) {
        return true;
    }
    if (node.val <= lower || node.val >= upper) {
        return false;
    }
    return helper(node.left,lower,node.val) && helper(node.right,node.val,upper);
}

helper方法解读:
当入参是左子树节点,要限制所有其他节点比该节点小(限制上界是节点val)
当入参是右子树节点,要限制所有其他节点比根节点大(限制下界是节点val)
省略树对象:见上一小节

测试:

@Test
public void test5(){
    System.out.println("--------------示例1--------------");
    TreeNode treeNode11 = new TreeNode(1);
    TreeNode treeNode33 = new TreeNode(3);
    TreeNode treeNode22 = new TreeNode(2,treeNode11,treeNode33);
    System.out.println(isValidBST(treeNode22));
    System.out.println("---------------示例2-------------");
    TreeNode treeNode1 = new TreeNode(1);
    TreeNode treeNode3 = new TreeNode(3);
    TreeNode treeNode6 = new TreeNode(6);
    TreeNode treeNode4 = new TreeNode(4, treeNode3, treeNode6);
    TreeNode treeNode5 = new TreeNode(5, treeNode1, treeNode4);
    System.out.println(isValidBST(treeNode5));
}

示例1(来自LeetCode):
在这里插入图片描述
示例2(来自LeetCode):
在这里插入图片描述
输出:

--------------示例1--------------
true
---------------示例2-------------
false

四、回溯

  1. 迭代所有可能的候选对象
  2. 试试这个部分候选解决方案
  3. 给定候选者,进一步探索
  4. 回溯

正如:LeetCode:22. 括号生成
在这里插入图片描述
图片来源

从上文的例子中,可以看出递归的题目都可以被画成树状图。本题要求是“有效的”括号组合,所以肯定不可能由右括号开始。之后就是尝试列举所有括号组合情况了,当括号数量达到 2n 这就是我们的终止递归的条件了。这里值得注意的是:左括号的数量不能大于n,而且右括号的数量不能大于左括号的数量,显然这样是不符合题目“有效的”括号组合规定的

public void backtrack(List<String> ans, StringBuilder cur, int open, int close, int max) {
    // 终止条件
    if (cur.length() == 2 * max) {
        ans.add(cur.toString());
        return;
    }
    // 左括号不能超过最大值
    if (open < max) {
        // 试探添加左括号
        backtrackV2(ans, cur.append("("), open + 1, close, max);
        // 回溯
        cur.deleteCharAt(cur.length() - 1);
    }
    // 右括号数量不能大于左括号数量
    if (close < open) {
        // 试探添加右括号
        backtrackV2(ans, cur.append(")"), open, close + 1, max);
        // 回溯
        cur.deleteCharAt(cur.length() - 1);
    }
}

public List<String> generateParenthesis(int n) {
    List<String> ans = new ArrayList<String>();
    backtrack(ans, new StringBuilder(), 0, 0, n);
    return ans;
}

测试

@Test
public void test6(){
    List<String> strings = generateParenthesis(3);
    System.out.println(strings);
}

输出:

[((())), (()()), (())(), ()(()), ()()()]

小名心得:在使用回溯法式,我们只需考虑清楚:方法外部的入参出参,方法内部的限制条件终止条件即可,无需过于关系代码是如何帮你实现的。也无需添加打印语句查看程序的执行顺序,那样只会越看越懵,所以我们只需考虑好,我们要怎样得到想要的结果即可。

参考内容:
【小小福讲算法】硅谷工程师十五分钟带你深入理解 Recursion (递归)算法,及其衍生出的算法(分治算法Divide and Conquer, 回溯 Backtracking)

参考内容中的大佬把递归讲的很好理解,感兴趣的小伙伴可以去看下(B站搬运版)。本文是小名学习后的总结,若有错误理解,希望大家可以在评论区纠正小名。感谢大家🙏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/65938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

这十一个副业在家就可以完成,疫情在家也有收入,建议收藏

2022年&#xff0c;谁还没有副业&#xff1f; 经过两年的疫情&#xff0c;我们都知道没有钱是一件非常不舒服的事情。现在的做法是&#xff1a;主营业务要求稳定&#xff0c;副业要求发展&#xff1b;好好发展副业是硬道理。 在过去的两年里&#xff0c;我一直在探索副业项目…

(六)Vue之数据代理

文章目录回顾Object.defineProperty方法数据属性valueenumerablewritableconfigurable访问器属性get()set(v: any)何为数据代理Vue中的数据代理Vue学习目录上一篇&#xff1a;&#xff08;六&#xff09;Vue之MVVC 回顾Object.defineProperty方法 Object.defineProperty方法的…

Chain Surfase Test - java 链表经典 OJ 面试题 - 巨细

效果图 LeetCode - 206. 反转链表 代码如下&#xff1a; /** Definition for singly-linked list. public class ListNode { int val;ListNode next;ListNode() {}ListNode(int val) { this.val val; }ListNode(int val, ListNode next) { this.val val; this.next next; …

K-Means++代码实现

K-Means代码实现 数据集 https://download.csdn.net/download/qq_43629083/87246495 import pandas as pd import numpy as np import random import math %matplotlib inline from matplotlib import pyplot as plt# 按文件名读取整个文件 data pd.read_csv(data.csv)class…

Minikube – 配置 Jenkins Kubernetes plugin

文章目录1. 配置 kubernetes credentials2. 安装 kubernets plugin3. 安装 docker 插件4. 连接 minikube 集群5. Pod template 参数6. Container template 参数7. 实例7.1 创建一个简单 pod7.2 pod name 变化7.3 指定 namespace7.4 volumes 挂载7.5 Liveness Probe 探针7.6 创建…

关于l2实时接口的功能分析

因为国内外股价的上涨都可以在界面上去查询&#xff0c;所以公司能准确地判断股价上涨&#xff0c;并适时买入、卖出&#xff0c;以此获得一定的利润。 l2实时接口还可以把以往的数据表示成一条折线&#xff0c;让公司在进行分析时更形象、更有参考意义。在连接界面后&#xf…

[附源码]Python计算机毕业设计Django校园订餐系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

星环科技TDS 2.4.0 发布: 数据开发、数据治理、数据运营套件能力再次升级

近日&#xff0c;星环科技大数据开发工具 Transwarp Data Studio 2.4.0版本重磅发布&#xff0c;新版本中数据开发、数据治理、数据运营三大套件能力全部升级&#xff0c;让数据开发更便捷、数据治理更高效、数据运营更智能。本次升级的核心能力如下&#xff1a; 数据开发套件…

【Opencv实战】高手勿入,Python使用Opencv+Canny实现边缘检测以及轮廓检测(详细步骤+源码分享)

前言 有温度 有深度 有广度 就等你来关注哦~ 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 在这次的案例实战中&#xff0c;我们将使用Python 3和OpenCV。我们将使用OpenCV&#xff0c;因为它是…

谷歌牛人发布小说式《算法图解》,竟被人扒下来,在GitHub开源了

今天给大家带来了一本算法方向的好书&#xff1a;巴尔加瓦&#xff08;Aditya Bhargava&#xff09;老师 著&#xff0c;袁国忠老师译的 《算法图解&#xff1a;像小说一样有趣的算法入门书》&#xff0c;网上有没有开源版本我不知道&#xff0c;我就看他内容不错所以推荐给大家…

蓄电池建模、分析与优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

[附源码]Python计算机毕业设计Django校园运动会管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

数据存储,详细讲解

✨数据存储&#xff0c;详细讲解&#x1f49c;数据类型的介绍&#xff1a;&#x1f499;整形的内存存储大小端介绍&#xff1a;&#x1f49b;浮点数的存储&#x1f49c;数据类型的介绍&#xff1a; 1.内置类型&#xff1a; char //字符数据类型&#xff08;1&#xff…

SpringBoot 之 AOP

前言&#xff1a; Spring 三大核心思想是啥&#xff0c;还记得不&#xff1f;IOC&#xff08;控制反转&#xff09;&#xff0c;DI&#xff08;依赖注入&#xff09;&#xff0c;AOP&#xff08;面向切面编程&#xff09;。回顾一下这三个东西&#xff1a; IOC&#xff1a;不考…

Dash初探:如何将Label和Dropdown放在一行

Use Dash! How to display html.Lable and dcc.Dropdown on the same line? 1、目标 下图展示了我想要实现的效果。 数据筛选部分包含了三个筛选条件&#xff1a;日期区间选择器&#xff1b;区域选择器&#xff1b;地市选择器。其中&#xff0c;地市选择器的取值和已选区域…

【1805. 字符串中不同整数的数目】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个字符串 word &#xff0c;该字符串由数字和小写英文字母组成。 请你用空格替换每个不是数字的字符。例如&#xff0c;"a123bc34d8ef34" 将会变成 " 123 34 8 34" 。注意…

[附源码]JAVA毕业设计砂石矿山管理系统(系统+LW)

[附源码]JAVA毕业设计砂石矿山管理系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

如何将Word转成PDF格式?这三种方法总有一个适合你

如何把Word文档转换成PDF文件格式呢&#xff1f;大家在传输文件的时候&#xff0c;相信很多小伙伴都喜欢不使用PDF文件格式&#xff0c;因为它非常的稳定&#xff0c;在浏览文件的时候格式不会乱&#xff0c;但我们都是先做好Word文档&#xff0c;然后再把它转成PDF格式&#x…

诗人贺伟陪你看世界杯

国内三大拥有转播权的主流平台&#xff08;央视、中国移动咪咕和抖音&#xff09;都已经公布了解说阵容 CCTV解说阵容 贺炜、刘嘉远、朱晓雨、曾侃、李晨明、孙思辰、邵圣懿。 此次央视世界杯采取了“单口”解说的模式&#xff0c;这多少还是会让球迷们感到错愕。以往的足球盛…

ubuntu+Docker双容器docker-compose部署Django+Vue项目(2-Django)

文章目录部署Django后端接口下载Python环境及一些尝试pip包管理运行项目容器报错1(查询容器IP解决)报错2(pvsite_uwsgi.ini文件配置socket还是http)报错3(用http先)用python manage.py runserver运行项目先报错4(下载cryptography库)回到用uwsgidjango运行成功先在uwsgidjango中…