(算法)深入理解递归

373 阅读2分钟

通过一道 代码随想录 的例题来理解递归

题目:求x的n次方

这是一道简单的题目,但又不是特别的简单,对于思维的训练是很好的题目

首先,想到的是直接使用for循环解决,时间复杂度是O(n),那么能不能考虑使用递归的方式解决呢?时间复杂度如何?递归的方式就一定时间复杂度高吗?递归是如何记录递归次数的?

在进行思考过后,我给出我对这道题的理解,结合代码和图,相信一定会有很大的帮助。

在这里称传统递归(简单一维)的递归,类似数据结构中的链表,每一个节点都是一个状态机,找到最后一个可以回溯的节点后开始返回,对应代码中的function2,对于下面的图,只需要数状态机的个数就可以知道递归的次数了,对应这边的时间复杂度就是O(n)

image.png

我们可以尝试使用二叉树结构尝试简化,看到这个结构,想到的就是难道是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),虽然没有成功优化,但是这种思维是好的

image.png

我们发现二叉树有对称的特性,既然是这样,按这一条路一直走下去,保存一个状态,相同冗余的状态只需要计算一次即可,只看绿色的部分,不就是O(logn)了,终于实现了

image.png 总结:上面的是每个状态机执行一次的情况,遇到复杂问题时,时间复杂度应该是每个状态机的时间复杂度 * 状态机的个数

下面是实现的代码,供参考

#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;
}