【C语言】经典的递归问题

简介: 【C语言】经典的递归问题

汉诺塔


汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。


 不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n).显然f(0)=0,f(1)=1,f(2)=3,f(3)=7,且f(n)=2*f(n-1)+1(n>=1)。此后不难证明f(n)=2^n-1。假设每秒钟移动一次,那么当n=64时,f(n)=18446744073709551615,将其转换成年,大约需要5849.42年。


 了解这个背景之后,我们来学习一下怎么用C语言来求解汉诺塔问题。


分析:


52af6bc1119b4e73a9660375eeb263ae.png


 其实我们可以发现其中圆盘的移动规律:


第一步:把n-1个圆盘由A移到B
第二步:把第n个圆盘由A移到C
第三步:把n-1个圆盘由B移到C

 整体的思路就是将n-1个盘子移到B柱上,然后再将最大的圆盘移到C柱上,最后将n-1个盘子移动到C柱上。

7abeec6a2b584558acca99577ca38dc7.png

 代码实现:


void Move(char Pos1, char Pos2)
{
  printf("%c->%c ", Pos1, Pos2);
}
//n:盘子的个数 Pos1:A起始位置 Pos2:B过渡位置 Pos3:C目的位置
void Hanoi(int n, char Pos1, char Pos2, char Pos3)
{
  if (n == 1)
  {
    Move(Pos1, Pos3);
  }
  else
  {
    Hanoi(n - 1, Pos1, Pos3, Pos2);//借助Pos3将盘子移动到Pos2
    Move(Pos1, Pos3);//将最大的盘子移动到Pos3
    Hanoi(n - 1, Pos2, Pos1, Pos3);
  }
}
int main()
{
  Hanoi(1, 'A', 'B', 'C');
  printf("\n");
  Hanoi(2, 'A', 'B', 'C');
  printf("\n");
  Hanoi(3, 'A', 'B', 'C');
  printf("\n");
  Hanoi(4, 'A', 'B', 'C');
  printf("\n");
  return 0;
}


#include <stdio.h>
void Hanoi_move(int n, char Pos1, char Pos2, char Pos3)
{
  if (n == 1)
  {
    printf("%c->%c ", Pos1, Pos3);
  }
  else
  {
    Hanoi_move(n - 1, Pos1, Pos3, Pos2);//借助Pos3将n-1个圆盘从Pos1移动到Pos2
    printf("%c->%c ", Pos1, Pos3);//将最大的圆盘从Pos1移动到Pos3
    Hanoi_move(n - 1, Pos2, Pos1, Pos3);//借助Pos1将n-1个圆盘从Pos2移动到Pos3
  }
}
int main()
{
  Hanoi_move(1, 'A', 'B', 'C');
  printf("\n");
  Hanoi_move(2, 'A', 'B', 'C');
  printf("\n");
  Hanoi_move(3, 'A', 'B', 'C');
  printf("\n");
  Hanoi_move(4, 'A', 'B', 'C');
  printf("\n");
  return 0;
}


输出结果:

79c7650c8126473fb0593c54dde83bec.png


汉诺塔移动次数的代码示例:


  因为我们知道汉诺塔移动次数的递推公式为f(n)=2*f(n-1)-1,所以递归的代码就变得很简单了。

#include <stdio.h>
long long Hanoi_sum(int n)
{
  //n为圆盘的个数
  if (n == 1)
    return 1;
  else
    return 2 * Hanoi_sum(n - 1) + 1;
}
int main()
{
  int n = 0;
  printf("请输入圆盘数:>");
  scanf("%d", &n);
  long long sum = Hanoi_sum(n);
  printf("sum = %lld\n", sum);
  return 0;
}


青蛙跳台阶


 一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级。求该青蛙跳上一个n 级的台阶总共有多少种跳法。其实青蛙跳台阶也是典型的斐波那契递归问题,现在我们就来分析一下。


题目分析:


 第一步,递推:目标是想求n级台阶有多少种走法,现在先假设已经走完了n级台阶同时假设存在f(n)种走法可以走完n级台阶,现在退回到走完这n级台阶的上一步,即走完这n级台阶的最后一步,最后一步有两种可能的情况,第一种情况是这一步只走了1级台阶,即完成最后一步之前已经走了n-1级台阶,那么走完n-1级台阶存在f(n-1)种走法;第二种情况是最后一步走了两级台阶,即完成最后一步之前已经走了n-2级台阶,那么走完n-2级台阶存在f(n-2)种走法。那么理一下思路:完成n级台阶最后一步之前需要走完n-1级台阶或者n-2级台阶,因为n级存在台阶f(n)种走法,n-1级台阶存在f(n-1)种走法,n-2级台阶存在f(n-2)种走法,所以f(n)=f(n-1)+f(n-2);继续递推,完成n-1级台阶的最后一步时之前肯定走了n-2或n-3级台阶,再继续递推,走完n-2级台阶的最后一步时之前肯定走了n-3或n-4级台阶,一直递推下去,则有:f(n) = f(n-1)+f(n-2) , f(n-1) = f(n-2)+f(n-3) , f(n-2) = f(n-3)+f(n-4) , f(n-3) = f(n-4)+f(n-5) , ...... , f(4) = f(3)+f(2) , f(3) = f(2)+f(1) 至此,递推结束。

 第二步,回归:分析上面递推中的表达式可知,f(n)的结果依赖于f(n-1)和f(n-2),f(n-1)的结果依赖于f(n-2)和f(n-3),每一项都是未知数,无法求解,只能一直依赖下去,直到最后依赖到f(1)和f(2)。也就是说只要f(1)和f(2)不是未知数,就可以逆着递推的顺序回归到f(n),解出f(n)。现在就要求f(1)和f(2),根据递推中的假设规律f(1)是指走1级台阶的走法,因为一级台阶只有一种走法,因此f(1)=1;f(2)是走2级台阶的走法,二级台阶的走法可以是先走一级再走一级或者直接走两级,因此f(2)=2。


 如果看懂了上面的分析,现在我们写代码就很简单了,就相当于求第n个斐波那契数是多少。


代码示例:


int Fib(int n)
{
  if (n <= 2)
    return 1;
  else
    return Fib(n - 1) + Fib(n - 2);
}
int main()
{
  int n = 0;
  printf("请输入台阶数:>");
  scanf("%d", &n);
  int ret = 0;
  ret = Fib(n);
  printf("跳完%d个台阶共有%d种跳法\n", n, ret);
  return 0;
}


 以上的两道题目就是非常经典的递归题目啦,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家啦!


结语


 每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根。

相关文章
|
1月前
|
机器学习/深度学习 C语言
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
【8月更文挑战第5天】本篇文章用C语言采用多文件编写实现了一个基础的扫雷游戏(附源码),并讲解了关于函数递归的基础概念及其相对应的习题练习(附源码)
33 1
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
|
23天前
|
机器学习/深度学习 C语言
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
要保持最小的步数,每一次汉诺塔问题(无论是最初还是递归过程中的),如果此时初始柱盘子数为偶数,我们第一步是把最上面的盘子移动到中转柱,如果为奇数,我们第一步则是将其移动到目标柱。
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
|
29天前
|
C语言
C语言中的递归
C语言中的递归
|
2月前
|
存储 编译器 C语言
|
22天前
|
算法 编译器 C语言
【C语言】递归
【C语言】递归
11 0
|
3月前
|
C语言
C语言--函数递归与迭代
C语言--函数递归与迭代
|
3月前
|
机器学习/深度学习 算法 C语言
详细介绍递归算法在 C 语言中的应用,包括递归的基本概念、特点、实现方法以及实际应用案例
【6月更文挑战第15天】递归算法在C语言中是强大力量的体现,通过函数调用自身解决复杂问题。递归涉及基本概念如自调用、终止条件及栈空间管理。在C中实现递归需定义递归函数,分解问题并设定停止条件。阶乘和斐波那契数列是经典应用示例,展示了递归的优雅与效率。然而,递归可能导致栈溢出,需注意优化。学习递归深化了对“分而治之”策略的理解。**
51 7
TU^
|
3月前
|
机器学习/深度学习 C语言
C语言之函数递归
C语言之函数递归
TU^
24 1
|
2月前
|
存储 算法 程序员
C语言编程—递归
递归是函数自我调用的编程技术,常用于解决分治问题,如计算阶乘和斐波那契数列。示例中展示了C语言的阶乘和斐波那契数列递归实现。递归需满足:问题可转化为规模更小的同类问题,存在结束条件以防止无限循环,并可能消耗大量时间和栈空间。栈用于存储函数调用信息,过多递归可能导致栈溢出。递归虽简洁,但非最优效率选择,递推算法通常是更好的替代方案。
33 0
|
3月前
|
C语言
【c语言】汉诺塔问题详解(c语言递归函数)
【c语言】汉诺塔问题详解(c语言递归函数)
18 0