递归小结(基础题目斐波那契,汉诺塔等)(下)

简介: 递归小结(基础题目斐波那契,汉诺塔等)

7.递归实现找到第n个斐波那契数


①.递归实现


递归题目里估计最著名的就是斐波那契数了,但是递归的写法其实并不高效,笔者进行了修改,使得各位可以看到它的弊端

//仅仅表达思想递归:
//斐波那契数列 1 1 2 3 5 8 13 21 34 55
int count = 0;
int Fib(int n)
{
  if (n == 4)
  count++;//计算重复度,重复度过大,时间浪费
  if (n <= 2)
  {
  return 1;
  }
  else
  return Fib(n - 1) + Fib(n - 2);//效率低是因为重复很多
}
int main()
{
  //第n个斐波那契数
  int n = 0;
  scanf("%d", &n);
  int fib = Fib(n);
  printf("%d\n", fib);
  printf("%d\n", count);
  return 0;
}


1669211808401.jpg


第三个数就是笔者测试的(笔者输入的是4)重复计算的个数高达接近20万.可见这个递归实现有多么低效。而且仅仅对于第50个数就需要很长时间计算。可以说十分鸡肋。


②非递归


int Fib(int n)
{
  int a = 1;
  int b = 1;
  int c = 0;
  while (n >= 3)
  {
  c = a + b;
  a = b;
  b = c;
  n--;//进来几次,算几次
  }
  return c;
}
int main()
{
  //第n个斐波那契数
  int n = 0;
  scanf("%d", &n);
  int fib=Fib(n);
  printf("%d\n", fib);
  return 0;
}

这个非递归实现还是挺有意思的。笔者利用图解可以让大家更加理解。


1669211825195.jpg


8.青蛙跳台阶问题


青蛙每次可以选择跳一个或者两个台阶,那么青蛙要跳到第n个台阶需要多少种跳法呢?


我们可以简单分析一下:


1669211839654.jpg


那么代码就可以实现:

int fib(int n)
{
    if (n <= 2)
        return n;
    else
    {
        return fib(n - 1) + fib(n - 2);
    }
}
int main() 
{
    int n = 0;
    scanf("%d", &n);
    printf("%d", fib(n));
    return 0;
}


9.逆向输出字符串


逆向输出字符串

//常规实现
void reverse_string(char* str)
{
  int count = 0;
  char* arr = str;
  while (*str != '\0')
  {
  count++;
  str++;
  }
  int left = 0;
  int right = count - 1;
  while(left <= right)
  {
  char tmp = arr[left];
  arr[left] = arr[right];
  arr[right] = tmp;
  left++;
  right--;
  }
}

递归实现:

#include<string.h>
void reverse_string(char* str)
{
  int count = strlen(str);
  char tmp = str[0];
  str[0] = str[count - 1];
  str[count - 1] = '\0';
  if (strlen(str+1) >= 2)
  {
  reverse_string(str + 1);
  }
  str[count - 1] = tmp;
}
int main()
{
  char arr[] = "qwertyu";
  reverse_string(arr);
  printf("%s\n", arr);
  return 0;
}


这个递归是如何操作的呢?首先笔者认为要根据这个字符串思考常规实现是如何转化为递归实现。很明显,基本思想是俩俩交换。见图


1669211874215.jpg


10.计算排列数


输入两个数,计算A(n上)(m下)😂。


#include<stdio.h>
int arrange(int n,int m)
{
    if(m)
    {
        return n*arrange(n-1,m-1);
    }
    else
        return 1;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int ret=arrange(n,m);
    printf("%d\n",ret);
    return 0;
}


1669211893062.jpg


11.汉诺塔问题(Hanoi)


题目笔者就不多赘述了。


图解:


1669211919123.jpg


代码实现:


//汉诺塔问题
void move(char pos1, char pos2)
{
  printf("%c->%c ", pos1, pos2);
}
void Hanoi(int n, char pos1, char pos2,char pos3)
{
  if (n == 1)
  {
  move(pos1, pos3);
  }
  else
  {
  Hanoi(n - 1, pos1, pos3, pos2);
  move(pos1, pos3);
  Hanoi(n - 1, pos2, pos1, pos3);
  }
}
int main()
{
  //笔者为了直观展示结果,多加了几个,其实只需一个就可以了
  int n,m,k;
  scanf("%d", &n);
  Hanoi(n, 'A','B', 'C');
  printf("\n");
  scanf("%d", & m);
  Hanoi(m, 'A', 'B', 'C');
  printf("\n");
  scanf("%d", &k);
  Hanoi(k, 'A', 'B', 'C');
  return 0;
}

对比图一下:


1669211934917.jpg


可以发现是可以吻合的。但是注意汉诺塔问题n值不能过大,通过图解诸君已经可以发现它的执行也是比较繁琐的,需要2^n-1次,指数爆炸还是挺恐怖的。


通过上面几个简单的题目就可以发现递归的好处,化繁为简。笔者认为,递归本身是比较抽象的,建议诸君在做这类题目的时候可以通过画图的形式进行剖析,那么就比较好解。要注意递归的限制条件和它的循环是怎么设计去接近这个条件,这一点是比较重要的。


但是递归也是有它的弊端:


递归层数太深会出现栈溢出的。

解决方案


1.递归改为非递归


2.非静态改为静态//不在栈上挪到静态区。

相关文章
|
6月前
|
算法 C语言
汉诺塔问题(利用递归解决)内含斐波那契数列0.o
汉诺塔问题(利用递归解决)内含斐波那契数列0.o
81 0
|
存储 算法
蓝桥杯:递归 与 例题:斐波那契数列及优化与应用
蓝桥杯:递归 与 例题:斐波那契数列及优化与应用
79 0
|
1月前
|
算法 定位技术
数据结构与算法学习九:学习递归。递归的经典实例:打印问题、阶乘问题、递归-迷宫问题、八皇后问题
本文详细介绍了递归的概念、重要规则、形式,并展示了递归在解决打印问题、阶乘问题、迷宫问题和八皇后问题等经典实例中的应用。
40 0
|
6月前
函数递归详解----跳台阶、斐波那契数列、汉诺塔问题
递归的思想:把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。递归中的递就是递推的意思,归就是回归的意思。
递归经典例题——汉诺塔
递归经典例题——汉诺塔
109 1
浅谈递归函数(最后一个例题:浅谈汉诺塔思路与代码)
浅谈递归函数(最后一个例题:浅谈汉诺塔思路与代码)
汉诺塔?爬楼梯?斐波那契?知道递归就够了
汉诺塔?爬楼梯?斐波那契?知道递归就够了
97 0
|
C语言
【C】青蛙跳台阶和汉诺塔问题(递归)
【C】青蛙跳台阶和汉诺塔问题(递归)
122 0
【C】青蛙跳台阶和汉诺塔问题(递归)
|
算法
【递归与递推 3】AcWing 717. 简单斐波那契(求解斐波那契数列的若干方法)
【递归与递推 3】AcWing 717. 简单斐波那契(求解斐波那契数列的若干方法)
【递归与递推 3】AcWing 717. 简单斐波那契(求解斐波那契数列的若干方法)
|
C语言
C语言题解:经典递归题目(斐波那契数列、汉罗塔问题以及青蛙跳台阶问题)
C语言题解:经典递归题目(斐波那契数列、汉罗塔问题以及青蛙跳台阶问题)
155 0
C语言题解:经典递归题目(斐波那契数列、汉罗塔问题以及青蛙跳台阶问题)