【记忆化搜索】斐波那契数

文章目录

题目链接:509. 斐波那契数


题目解析

记忆化搜索 是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式(是一种常见的动态规划实现方式)。

可以通过记忆化搜索来优化 DFS(剪枝)以提高算法效率。

文章将通过斐波那契数这道题目来呈现记忆化搜索具体如何优化。

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。

也就是:

  • F(0) = 0,F(1) = 1
  • F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,我们需要返回计算后的 F(n)


DFS:深度优先搜索

算法原理

很经典的递归操作,通过编写 dfs 函数递归计算第 n 个斐波那契数。

dfs 函数

dfs 的任务是:根据斐波那契数的定义返回第 n 个斐波那契数(若 n 为 0 或 1,则返回 n,否则返回 F(n-1) + F(n-2)),参数是 n,返回值为 int。

java 复制代码
int dfs (int n);

代码实现

java 复制代码
class Solution {
    public int fib(int n) {
        // 搜索
        return dfs(n);
    }

    public int dfs (int n) {
        // 递归出口
        if (n == 0 || n == 1) {
            return n;
        }

        return dfs(n-1) + dfs(n-2);
    }
}

记忆化搜索

DFS 方法写的代码虽然简洁,但是实际算法的计算过程并不快,相反,存在很多重复计算的步骤,导致整体的时间复杂度达到 O(2n)。

给出一个例子:n = 5,求 F(5)

画出递归展开图如下:
#mermaid-svg-c3bn2m3W9LHnVzW7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-c3bn2m3W9LHnVzW7 .error-icon{fill:#552222;}#mermaid-svg-c3bn2m3W9LHnVzW7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-c3bn2m3W9LHnVzW7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .marker.cross{stroke:#333333;}#mermaid-svg-c3bn2m3W9LHnVzW7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-c3bn2m3W9LHnVzW7 p{margin:0;}#mermaid-svg-c3bn2m3W9LHnVzW7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .cluster-label text{fill:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .cluster-label span{color:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .cluster-label span p{background-color:transparent;}#mermaid-svg-c3bn2m3W9LHnVzW7 .label text,#mermaid-svg-c3bn2m3W9LHnVzW7 span{fill:#333;color:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node rect,#mermaid-svg-c3bn2m3W9LHnVzW7 .node circle,#mermaid-svg-c3bn2m3W9LHnVzW7 .node ellipse,#mermaid-svg-c3bn2m3W9LHnVzW7 .node polygon,#mermaid-svg-c3bn2m3W9LHnVzW7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .rough-node .label text,#mermaid-svg-c3bn2m3W9LHnVzW7 .node .label text,#mermaid-svg-c3bn2m3W9LHnVzW7 .image-shape .label,#mermaid-svg-c3bn2m3W9LHnVzW7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .rough-node .label,#mermaid-svg-c3bn2m3W9LHnVzW7 .node .label,#mermaid-svg-c3bn2m3W9LHnVzW7 .image-shape .label,#mermaid-svg-c3bn2m3W9LHnVzW7 .icon-shape .label{text-align:center;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node.clickable{cursor:pointer;}#mermaid-svg-c3bn2m3W9LHnVzW7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .arrowheadPath{fill:#333333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c3bn2m3W9LHnVzW7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-c3bn2m3W9LHnVzW7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c3bn2m3W9LHnVzW7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-c3bn2m3W9LHnVzW7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .cluster text{fill:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 .cluster span{color:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-c3bn2m3W9LHnVzW7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-c3bn2m3W9LHnVzW7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-c3bn2m3W9LHnVzW7 .icon-shape,#mermaid-svg-c3bn2m3W9LHnVzW7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c3bn2m3W9LHnVzW7 .icon-shape p,#mermaid-svg-c3bn2m3W9LHnVzW7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-c3bn2m3W9LHnVzW7 .icon-shape .label rect,#mermaid-svg-c3bn2m3W9LHnVzW7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c3bn2m3W9LHnVzW7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-c3bn2m3W9LHnVzW7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node>*{fill:#FFF4CC!important;stroke:#D4B884!important;stroke-width:1.5px!important;color:#000!important;rx:5!important;ry:5!important;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node span{fill:#FFF4CC!important;stroke:#D4B884!important;stroke-width:1.5px!important;color:#000!important;rx:5!important;ry:5!important;}#mermaid-svg-c3bn2m3W9LHnVzW7 .node tspan{fill:#000!important;} F(5)
F(4)
F(3)
F(3)
F(2)
F(2)
F(1)
F(2)
F(1)
F(1)
F(0)
F(1)
F(0)
F(1)
F(0)

可以发现,就算 n = 5 这么简单的计算,画出来的二叉树的高度也达到了 5,而且可以发现许多重复计算的部分:F(3) 和 F(2)。
#mermaid-svg-tF1mo0b1HCPdcIm1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tF1mo0b1HCPdcIm1 .error-icon{fill:#552222;}#mermaid-svg-tF1mo0b1HCPdcIm1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tF1mo0b1HCPdcIm1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .marker.cross{stroke:#333333;}#mermaid-svg-tF1mo0b1HCPdcIm1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tF1mo0b1HCPdcIm1 p{margin:0;}#mermaid-svg-tF1mo0b1HCPdcIm1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .cluster-label text{fill:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .cluster-label span{color:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .cluster-label span p{background-color:transparent;}#mermaid-svg-tF1mo0b1HCPdcIm1 .label text,#mermaid-svg-tF1mo0b1HCPdcIm1 span{fill:#333;color:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .node rect,#mermaid-svg-tF1mo0b1HCPdcIm1 .node circle,#mermaid-svg-tF1mo0b1HCPdcIm1 .node ellipse,#mermaid-svg-tF1mo0b1HCPdcIm1 .node polygon,#mermaid-svg-tF1mo0b1HCPdcIm1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .rough-node .label text,#mermaid-svg-tF1mo0b1HCPdcIm1 .node .label text,#mermaid-svg-tF1mo0b1HCPdcIm1 .image-shape .label,#mermaid-svg-tF1mo0b1HCPdcIm1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tF1mo0b1HCPdcIm1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .rough-node .label,#mermaid-svg-tF1mo0b1HCPdcIm1 .node .label,#mermaid-svg-tF1mo0b1HCPdcIm1 .image-shape .label,#mermaid-svg-tF1mo0b1HCPdcIm1 .icon-shape .label{text-align:center;}#mermaid-svg-tF1mo0b1HCPdcIm1 .node.clickable{cursor:pointer;}#mermaid-svg-tF1mo0b1HCPdcIm1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .arrowheadPath{fill:#333333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tF1mo0b1HCPdcIm1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tF1mo0b1HCPdcIm1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tF1mo0b1HCPdcIm1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tF1mo0b1HCPdcIm1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .cluster text{fill:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 .cluster span{color:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tF1mo0b1HCPdcIm1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tF1mo0b1HCPdcIm1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tF1mo0b1HCPdcIm1 .icon-shape,#mermaid-svg-tF1mo0b1HCPdcIm1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tF1mo0b1HCPdcIm1 .icon-shape p,#mermaid-svg-tF1mo0b1HCPdcIm1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tF1mo0b1HCPdcIm1 .icon-shape .label rect,#mermaid-svg-tF1mo0b1HCPdcIm1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tF1mo0b1HCPdcIm1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tF1mo0b1HCPdcIm1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tF1mo0b1HCPdcIm1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-tF1mo0b1HCPdcIm1 .yellow>*{fill:#FFF4CC!important;stroke:#D4B884!important;stroke-width:1.5px!important;color:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .yellow span{fill:#FFF4CC!important;stroke:#D4B884!important;stroke-width:1.5px!important;color:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .yellow tspan{fill:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .red>*{fill:#FCE4E4!important;stroke:#E6B8B8!important;stroke-width:1.5px!important;color:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .red span{fill:#FCE4E4!important;stroke:#E6B8B8!important;stroke-width:1.5px!important;color:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .red tspan{fill:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .purple>*{fill:#EAE6F8!important;stroke:#B3A2D9!important;stroke-width:1.5px!important;color:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .purple span{fill:#EAE6F8!important;stroke:#B3A2D9!important;stroke-width:1.5px!important;color:#000!important;}#mermaid-svg-tF1mo0b1HCPdcIm1 .purple tspan{fill:#000!important;} F(5)
F(4)
F(3)
F(3)
F(2)
F(2)
F(1)
F(1)
F(0)
F(1)
F(0)
F(2)
F(1)
F(1)
F(0)

如果将之前已经展开那部分的返回值存入一个 备忘录,在下一次展开之前先搜索备忘录,就可以避免再次展开递归直接返回之前计算过的结果,从而剪枝、提高效率,算法的时间复杂度为 O(n)。

这就是 记忆化搜索 ,可以将其理解为 "带着记忆搜索" ,而这里的记忆主要依靠 "备忘录" (可以是哈希表、数组,是能够映射可变参数和返回值的一种数据结构)。

记忆数组

为了递归方便,将备忘录 memo 定义成全局的。

该备忘录在本题中可以是一个数组 ,其下标 n 表示第 n 个斐波那契数,而下标对应的值则是具体的第 n 个斐波那契数的值 F(n)

java 复制代码
int[] memo;

在 DFS 开始之前,首先要对这个记忆数组进行初始化,将其中的值全部初始化为 -1,表示还没有存入搜索展开的值。

如何使用这个记忆数组呢?

  1. 在每一次递归展开之前,先查看备忘录,若当前可变参数 n 的值已经存在,直接返回该值
  2. 在每一次递归展开之后、返回上层之前,先将当前展开后的可变参数对应的值存入备忘录

代码实现

java 复制代码
class Solution {
    int[] memo = new int[31]; // 备忘录

    public int fib(int n) {
        // 初始化备忘录
        Arrays.fill(memo, -1);
        // 搜索
        return dfs(n);
    }

    public int dfs (int n) {
        // 在展开之前查看备忘录
        if (memo[n] != -1) {
            return memo[n];
        }

        // 递归出口
        if (n == 0 || n == 1) {
            memo[n] = n;       // 返回之前先存入备忘录
            return n;
        }

        // 展开并返回
        memo[n] = dfs(n-1) + dfs(n-2); // 返回之前先存入备忘录
        return memo[n];
    }
}

文章到这里就告一段落啦,若有错误请尽管指出~

相关推荐
落魄大学生之流水线上谋生计1 小时前
JVM内存结构、垃圾回收算法与类加载机制详解
jvm·算法
. . . . .1 小时前
comfyUI原理
人工智能·算法·机器学习
淡海水1 小时前
10-05-高级-模式匹配-CSharp如何改变与数据结构的交互方式
数据结构·算法·c#·模式匹配
geovindu1 小时前
java: Task Scheduler
java·开发语言·后端
神奇小梵1 小时前
java的特殊语法点,与常规语言相异的点
java·开发语言
果壳science1 小时前
张祥前统一场论研讨会在香港理工大学举办
人工智能·算法
无巧不成书02181 小时前
Tomcat 下载后使用全指南:解压、环境配置、启动验证与常见问题排查
java·tomcat
Wang's Blog1 小时前
Java框架快速入门: Spring Security+OAuth2之多因子认证缓存服务
java·spring·缓存
桐盛科技2 小时前
能碳数据清洗用MAD算法,窗口大小和阈值怎么调?
物联网·算法·边缘计算