通过一道 代码随想录 的例题来理解递归
题目:求x的n次方
这是一道简单的题目,但又不是特别的简单,对于思维的训练是很好的题目
首先,想到的是直接使用for循环解决,时间复杂度是O(n),那么能不能考虑使用递归的方式解决呢?时间复杂度如何?递归的方式就一定时间复杂度高吗?递归是如何记录递归次数的?
在进行思考过后,我给出我对这道题的理解,结合代码和图,相信一定会有很大的帮助。
在这里称传统递归(简单一维)的递归,类似数据结构中的链表,每一个节点都是一个状态机,找到最后一个可以回溯的节点后开始返回,对应代码中的function2,对于下面的图,只需要数状态机的个数就可以知道递归的次数了,对应这边的时间复杂度就是O(n)
我们可以尝试使用二叉树结构尝试简化,看到这个结构,想到的就是难道是O(logn),数状态机的个数,2^0+2^1+2^2 = 2^3-1(深度m从0开始)
提炼公式 2^0 + 2^1 + ... + 2^m = 2^(m+1)-1 1⃣️
m = logn - 1 2⃣️
2⃣️带入1⃣️,发现状态机的个数是n-1,时间复杂度其实是O(n),虽然没有成功优化,但是这种思维是好的
我们发现二叉树有对称的特性,既然是这样,按这一条路一直走下去,保存一个状态,相同冗余的状态只需要计算一次即可,只看绿色的部分,不就是O(logn)了,终于实现了
总结:上面的是每个状态机执行一次的情况,遇到复杂问题时,时间复杂度应该是每个状态机的时间复杂度 * 状态机的个数
下面是实现的代码,供参考
#include<iostream>
#include<cstdio>
//题目:求x的n次方
using namespace std;
int function1(int x, int n)
{
int res = 1; // 任何数的0次方等于1
for(int i = 0 ; i<n; ++i){
res = res * x;
}
return res;
}
int function2(int x,int n)
{
//O(n)的递归的方式
if (n == 0)return 1;
return function2(x,n-1) * x;
}
int function3(int x,int n)
{
//O(n)的方式,比如将x^8先拆成二叉树的形式x^4 * x^4
if(n == 0)return 1; //任何数的0次方都是1
if(n % 2 == 1){
return function3(x,n/2) * function3(x,n/2) * x; // 15变成两个7还少个1
}
return function3(x,n/2) * function3(x,n/2); // 偶数
}
int function4(int x,int n)
{
// 时间复杂度是O(logn)
if(n == 0 )return 1;
int t = function4(x,n/2);
if(n % 2 == 1){
//奇数
return x * t * t;
}
return t * t;
}
int main()
{
int x,n;
cin >> x >>n;
cout << "function1:"<<x<<"的"<<n<<"次方结果是" << function1(x,n) << endl;
cout << "function2:"<<x<<"的"<<n<<"次方结果是" << function2(x,n) << endl;
cout << "function4:"<<x<<"的"<<n<<"次方结果是" << function4(x,n) << endl;
}