首页 > 其他分享 >简单聊聊:递归,缓存,分治,回溯

简单聊聊:递归,缓存,分治,回溯

时间:2023-01-03 12:57:00浏览次数:52  
标签:缓存 TreeNode val int new 聊聊 回溯 return 节点

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

例子:小名跑步。
例如:小名跑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
小名跑完了全程!

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

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回溯:逐步尝试所有满足条件的结果,一旦发现不可行的解,立即停止。

二、缓存
初始化缓存
如果缓存中存在答案,则直接返回
将计算结果写入缓存
正如: 斐波那契数

 

题目提示中提到:
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
三、分治
把大问题分为一系列小问题
递归求解每个子问题
合并每个子问题的结果
二叉搜索树(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:

示例2:

输出:

--------------示例1--------------
true
---------------示例2-------------
false
1
2
3
4
四、回溯
迭代所有可能的候选对象
试试这个部分候选解决方案
给定候选者,进一步探索
回溯
正如: 括号生成

 

从上文的例子中,可以看出递归的题目都可以被画成树状图。本题要求是“有效的”括号组合,所以肯定不可能由右括号开始。之后就是尝试列举所有括号组合情况了,当括号数量达到 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);
}

输出:

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

标签:缓存,TreeNode,val,int,new,聊聊,回溯,return,节点
From: https://www.cnblogs.com/yongheng1886/p/17021749.html

相关文章

  • 简单聊聊:Stream.reduce()用法解析
    基本使用先举一个简单的例子:算法题:Words题目描述每个句子由多个单词组成,句子中的每个单词的长度都可能不一样,我们假设每个单词的长度Ni为该单词的重量,你需要做的就是给出......
  • 聊聊业务项目如何主动感知mysql是否存活
    前言先前写过一篇文章聊聊如何利用redis实现多级缓存同步,里面讲到业务部门因数据库宕机,有技术提出当数据库宕机,切换到redis,今天我们就来聊聊如何触发这个切换动作?1、方......
  • webpack4.15.1 学习笔记(八) — 缓存(Caching)
    目录输出文件名(OutputFilenames)缓存第三方库将js文件放到一个文件夹中 webpack打包模块化后的应用程序,会生成一个可部署的/dist目录,只要/dist目录中的内......
  • http强制缓存、协商缓存、指纹ETag详解
    目录实操目录及步骤缓存分类强制缓存对比缓存指纹Etag摘要及加密算法缓存总结 每个浏览器都有一个自己的缓存区,使用缓存区的数据有诸多好处,减少冗余的......
  • Java:SpringBoot整合Redis实现数据缓存
    目录结构$tree.├──pom.xml└──src├──main│├──java││└──com││└──example││......
  • 2023新年第一篇随笔(聊聊基金理财)
    2023年1月1日,是新年的第一天,祝大家新年快乐!今天突然想写点东西,就主要围绕去年刚接触的基金谈起吧,2022年4月9号,我在B站无意中发现up遇见狂神说(up是我大学生活中......
  • 详解前端缓存,解决前端换包之后环境中仍会出现旧版效果
    前端项目修改了很多东西:比如bug啊,样式啊。当你把前端项目打包之后满心欢喜的在Nginx(测试环境)换上它,然后在Jira上修改bug状态@测试人员复测。然后测试人员开始找你ba......
  • 聊聊损失函数1. 噪声鲁棒损失函数简析 & 代码实现
    今天来聊聊非常规的损失函数。在常用的分类交叉熵,以及回归均方误差之外,针对训练样本可能存在的数据长尾,标签噪声,数据不均衡等问题,我们来聊聊适用不同场景有针对性的损失函......
  • 代码随想录——回溯算法
    组合题目中等classSolution{List<List<Integer>>result=newArrayList<>();LinkedList<Integer>path=newLinkedList<>();publicList<List<Int......
  • 缓存工具类
    packagecom.hmdp.utils;importcn.hutool.core.util.BooleanUtil;importcn.hutool.core.util.StrUtil;importcn.hutool.json.JSONObject;importcn.hutool.json.JS......