记忆化搜索其实就是带着"备忘录"的递归。通过"备忘录"实现递归的剪枝,提高递归效率。
斐波那契数

思路: 这里通过记忆化搜索(自顶向下的动态规划) 思想解决斐波那契数列问题,核心思路为:
首先,为了避免传统递归求解斐波那契数列时大量重复计算子问题(比如计算fib(5)时会重复计算fib(3)、fib(2)等),代码引入一个长度为n+1的数组memory作为记忆容器,初始值设为-1,用于存储已经计算过的斐波那契数值,标记 "未计算" 状态。
然后,定义递归函数dfs实现核心计算逻辑:递归过程中先检查memory[n],若不为-1,说明该位置的斐波那契值已计算过,直接返回该值,避免重复递归;若为-1,则先处理边界条件(n=0或n=1时,斐波那契值为自身),将结果存入memory后返回;对于其他情况,递归计算n-1和n-2的斐波那契值,求和后存入memory[n],再返回该结果。
整个过程通过 "先查记忆容器,再递归计算,最后存储结果" 的方式,将原本指数级时间复杂度的纯递归优化为线性时间复杂度,既保留了递归的直观性,又通过记忆化消除了重复计算。
算法:
cpp
class Solution {
public:
int fib(int n) {
vector<int> memory(n + 1, -1);
return dfs(memory, n);
}
int dfs(vector<int>& memory, int n)
{
if(memory[n] != -1)
return memory[n];
if(n == 0 || n == 1)
{
memory[n] = n;
return n;
}
memory[n] = dfs(memory, n - 1) + dfs(memory, n - 2);
return memory[n];
}
};
不同路径

**思路:**这道题采用记忆化搜索(带备忘录的深度优先搜索)来解决。
首先明确从网格右下角 (m,n) 到左上角 (1,1) 的路径数等价于从 (1,1) 到 (m,n) 的路径数,且到达 (m,n) 的路径数等于到达其上方位置 (m-1,n) 的路径数加上到达其左侧位置 (m,n-1) 的路径数(因为只能向右 / 向下走,反向则为向上 / 向左);
为避免递归过程中重复计算相同位置的路径数,引入二维备忘录memory存储已计算出的位置路径数,递归时先检查备忘录,若已有值则直接返回,无需重复计算;
同时设定边界条件:当 m 或 n 为 0 时(超出网格范围)路径数为 0,当 m 和 n 都为 1 时(起点)路径数为 1,最终通过递归累加求出从 (1,1) 到 (m,n) 的所有唯一路径数。
代码:
cpp
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> memory(m + 1, vector<int>(n + 1, 0)); //记忆化搜索中的备忘录
return dfs(m, n, memory);
}
int dfs(int m, int n, vector<vector<int>>& memory)
{
if(memory[m][n] != 0)
return memory[m][n];
if(m == 0 || n == 0)
return 0;
if(m == 1 && n == 1)
{
memory[m][n] = 1;
return 1;
}
memory[m][n] = dfs(m - 1, n, memory) + dfs(m, n - 1, memory);
return memory[m][n];
}
};
最长递增子序列

**思路:**这道题用记忆化搜索来解决。
定义dfs(pos)表示以数组中第pos个元素为起点的最长递增子序列长度,为避免递归中重复计算相同位置的结果,引入一维备忘录memory存储每个位置的计算结果;
递归时先检查备忘录,若memory[pos]不为 0 则直接返回该值,否则初始化当前最长长度为 1(仅包含自身),然后遍历pos之后的所有位置i,若nums[i] > nums[pos],说明可以将nums[i]接在nums[pos]后形成更长的递增子序列,此时递归计算dfs(i)并加 1,与当前最长长度取最大值;
计算完成后将结果存入memory[pos],最后遍历数组所有位置作为起点调用dfs,取其中的最大值即为整个数组的最长递增子序列长度。
代码:
cpp
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> memory(n, 0);
int ret = 0;
for(int i = 0; i < n; i++)
{
ret = max(ret, dfs(i, nums, memory));
}
return ret;
}
int dfs(int pos, vector<int>& nums, vector<int>& memory)
{
if(memory[pos] != 0)
return memory[pos];
int ret = 1;
for(int i = pos + 1; i < nums.size(); i++)
{
if(nums[i] > nums[pos])
{
ret = max(ret, dfs(i, nums, memory) + 1);
}
}
memory[pos] = ret;
return ret;
}
};
猜数字大小 II


思路: 猜数字时,若选择数字 k,则需付出 k 的成本,且必然面临两种结果 ------ 猜大了(需在[1, k-1]继续猜)或猜小了(需在[k+1, n]继续猜);由于要 "确保猜对",必须按最坏情况(即成本更高的那个子区间)计算总成本,而我们的目标是在所有可选的 k 中,找到能让这个 "最坏成本" 最小的那个值。
整个区间[1, n]的最小最坏成本,可拆解为子区间[1, k-1]和[k+1, n]的最小最坏成本加上 k 值,k 可以是[1, n] 区间中的任意一个值,所以需要枚举 k 为每一个值的情况,然后找到所有确保胜利的情况中最小的成本。因为无论选哪个k,后续的子问题都需要用同样的逻辑求解,因此不同的父问题可能会重复求解同一个子区间(比如计算[1,5]时会用到[2,4],计算[2,5]时也会用到[2,4]),如果每次都重新计算子区间的解,会产生大量重复运算;因此还需要通过记忆化方式存储已计算过的区间结果,优化效率。
代码:
cpp
class Solution {
int memory[201][201];
public:
int getMoneyAmount(int n) {
return dfs(1, n);
}
int dfs(int left, int right)
{
if(left >= right)
return 0;
if(memory[left][right] != 0)
return memory[left][right];
int ret = INT_MAX;
for(int head = left; head <= right; head++)
{
int x = dfs(left, head - 1);
int y = dfs(head + 1, right);
ret = min(head + max(x, y), ret);
}
memory[left][right] = ret;
return ret;
}
};
矩阵中的最长递增路径

思路: 要找到矩阵中最长的递增路径,首先定义递归函数 dfs(matrix,i, j) 表示从 matrix 矩阵中 (i,j) 位置出发能找到的最长递增路径长度;为避免重复计算,用二维数组 memory 缓存每个位置的计算结果,若该位置已缓存则直接返回;初始时每个位置自身构成长度为 1 的路径,随后遍历该位置上下左右四个方向的相邻位置,若相邻位置坐标合法且数值大于当前位置,则递归计算该相邻位置的最长递增路径长度并加 1,取所有合法方向中的最大值作为当前位置的最长路径长度,存入缓存后返回;最后遍历矩阵中每一个位置,调用 dfs 函数并记录所有结果的最大值,即为整个矩阵的最长递增路径长度。
代码:
cpp
class Solution {
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int m, n;
int memory[201][201];
public:
int longestIncreasingPath(vector<vector<int>>& matrix) {
m = matrix.size();
n = matrix[0].size();
int ret = 0;
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
ret = max(ret, dfs(matrix, i, j));
}
}
return ret;
}
int dfs(vector<vector<int>>& matrix, int i, int j)
{
if(memory[i][j] != 0)
return memory[i][j];
int ret = 1;
for(int k = 0; k < 4; k++)
{
int x = i + dx[k];
int y = j + dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j])
{
ret = max(ret, dfs(matrix, x, y) + 1);
}
}
memory[i][j] = ret;
return memory[i][j];
}
};