至于为何用C语言来开荒算法,我不知道我不知道!!!(无尽的后悔)
但是既然你从标题进来了,那就说明你也准备用C写记忆化搜索,那么我就以下面一题为例,讲解一下C实现记忆化搜索的办法。
这是LeetCode的第70题,爬梯子:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
我的解法:
一般看到这种题目都想先使用递归去做,因为递归最容易理解。首先我们自顶向下分析,当n=6,那么第六级阶梯可能从第五级上来,也可能是第四级。以此类推,图解如下:
叶子节点只可能是F(2)或者F(1),很明显F(1)和F(2)的值为1和2,那么我们的递推公式就很清楚了,就是
因此,代码应该不难写出:
int climbStairs(int n){
if(n == 1) return 1;
if(n == 2) return 2;
return climbStairs(n - 1) + climbStairs(n - 2);
}
但是,这个的时间复杂度很高,达到了惊人的O(n²),因此,我们需要进行优化,这里就需要用到我们所需要的记忆化搜索的方法
这里我们需要建立一个键值对,我的想法是,使用数组的下标存储其第几级时的次数,那么就可以少遍历几次,就如下图,红框里的我们算过就不用再算了。
那么我们就可以这样写出代码。
const int inf = -1;
int *data,flag = 1,Max;
int climbStairs(int n)
{
if(n <= 2) return n;
if(flag)
{
init(n);
flag = 0;
}
if(data[n] != inf)
return data[n];
else{
int result = climbStairs(n - 1) + climbStairs(n - 2);
data[n] = result;
return result;
}
}
void init(int n)
{
data = malloc(sizeof(int*)*n);
memset(data,inf,sizeof(int*)*n);
}
先给Memory数组赋初值,然后给做过的数组里塞入数据,使得第几级的数据被“记住”。因为次数不可能存在0,那么实现起来就很容易啦!这样子的缺点就是当数据规模很大的时候会占用一大片内存,那么我们还有什么方法呢?
int Memory[45 + 1] = {0,1,2};
int climbStairs(int n){
if (Memory[n] != 0) return Memory[n];
return Memory[n] = climbStairs(n - 1) + climbStairs(n - 2), Memory[n];
}
直接预估定长数组即可(要睡觉了摆了,有好想法的可以留言,谢谢)。