斐波那契数列的逐步优化 + 宏中#和##的区别

算法

斐波那契数列的逐步优化
问题本身不难,主要体会优化的过程。

什么是斐波那契数列:
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
满足这个公式的数列就称为斐波那契数列。

最简单的实现方式——递归
我们容易想到的斐波那契数列的求法就是递归,当然递归也是最简单的解决办法,那么先从递归开始看。

递归实现斐波那契数列的代码如下:

int fid(int n)
{
	if(n == 1 || n == 2) return 1;
	return fid(n-1) + fid(n-2);
}

假设上述代码N传20,可以画出递归树如下:
在这里插入图片描述
可以看出有很多重复计算,再来计算一下它的时间复杂度,有节点的个数很明显是2n-1个在指数级别,一个节点对应一次运算所以是O(1),所以它的时间复杂度就是O(2n)。

优化思路——去重复
我们可以做一个“备忘录”将计算过的节点记录,当下次访问计算过的节点时直接拿来用。
加了“备忘录”的代码如下:

int fid(int n)
{
	if(n == 1 || n == 2) return 1;
	vector<int> data(n+1, 0); // 第一位空出来不用
	return fid(n, data);
}
int fid(int n, vector<int>& data)
{
	if(n == 1 || n == 2) return 1;
	if(data[n] != 0) return data[n]; // 先判断,没有计算过再计算
	else data[n] = fid(n-1, data) + fid(n-2, data);
	return data[n];
}

加了“备忘录”递归图如下:
在这里插入图片描述
如此一来就省略了很多次重复的运算,因为“备忘录”的作用,将原始的递归树进行了裁剪。
再看此时的时间复杂度:有节点个数与n是成正比的所以子问题个数为O(n),解决一个子问题没有循环所以是O(1),所以此时的时间复杂度为O(n)。
将指数级的时间复杂度降到了线性级别。但是递归总要开辟栈空间,同时我们还新增了一个“备忘录”大小为n+1 此时的空间复杂度是O(n2)。
所以我们可以继续优化:

上述本质上是自顶向下的解决问题,那么我们可以反过来自底向上的推导。
既然递归是自顶向下并且开辟额外的栈空间那么就避免递归。
使用循环实现代码如下:

int fid(int n)
{
	if(n == 1 || n == 2) return 1;
	vector<int> data(n+1, 0); 
	data[1] = 1;
	data[2] = 1;
	for(int i = 3; i <= n; ++i){
		data[i] = data[i-1] + data[i-2];
	}
	return data[n];
}

这样以来时间和空间复杂度都为O(n)

接下来就是最后一步优化了:空间复杂度优化为O(1)
代码如下:

int fid(int n)
{
	if(n == 1 || n == 2) return 1;
	int first = 1;
	int second = 1;
	int res = 0;
	for(int i = 3; i <= n; ++i){
		res = first + second;
		first = second;
		second = res;
	}
	return res;
}

面试题

宏中#和##的区别
“#”表示宏替换为字符串
“##”表示拼接为字符串

使用宏函数演示代码如下:

#define FUN(x) #x  // 将x替换为"x"字符串
#define TFUN(x+y) x##y // 将x和y拼接为字符串,不管调用宏函数时的x和y是什么类型
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值