前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >汉诺塔问题(利用递归解决)内含斐波那契数列0.o

汉诺塔问题(利用递归解决)内含斐波那契数列0.o

作者头像
用户11039545
发布于 2024-03-28 09:20:41
发布于 2024-03-28 09:20:41
20400
代码可运行
举报
文章被收录于专栏:c语言c语言
运行总次数:0
代码可运行

首先,我们来看看什么是汉诺塔吧~记得初知汉诺塔,就是在今年的暑假游览科技馆的时候,里面就有汉诺塔的游戏,当然耐心烦躁的我并没有解决,没想到今日学习c语言还能看见它(捂脸)。

汉诺塔介绍

传说印度教主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。(以上为废话)

在C语言中,可以使用递归算法来实现汉诺塔问题。汉诺塔问题是一个经典的递归问题,规定了三根柱子(A、B、C),其中A柱上有n个圆盘,按照从大到小的顺序堆叠。问题的目标是将这些圆盘从A柱移到C柱,并且在移动过程中要遵循以下规则:

1.每次只能移动一个圆盘。 2.大圆盘不能放在小圆盘上面。

那么,我们如何将64片金片移动到另一根针上呢?要解决这个问题,我们需要了解递归的相关知识。

递归知识点讲解

递归就是栈思想的应用。递归简单来说就是写一个函数,自己调用自己。

例如,一个函数就是它的语句块,在c语言里函数的执行都是从上往下的。当这个函数自己调用自己的时候,代码块从上往下的执行便会中断。代码块会被插入一个代码块,然后再执行这个代码块。以此类推,每次执行这个代码块调用到自身语句都会被插入又一个代码块。

每一个递归函数都有一个临界点,到达这个临界点时停止调用自己,这样函数就能执行被打断调用的语句了。

递归的优点是算法简单、容易理解,代码行数少。

递归的一个缺点就是存在大量的重复计算,运行起来浪费时间也浪费空间。

递归的另一个缺点是递归的层数不能太多(不能递归太深)。那递归得太深了会怎样呢?答案是会爆栈。(以上内容为引用,我并不能理解爆栈的意思,希望有人可以给我解释一下~~)

再看一下递归函数的构成 以n!为例

来个栗子:写一个简单的递归函数

代码语言:javascript
代码运行次数:0
运行
复制
void f(int n)//函数返回类型 函数名(参数)
{if(n==1)
      printf("1");
      return;
}
else 
{
      f(n-1);
      printf("%d",n);
}
}

这是一个打印出1到n数字的函数。那么,这个递归函数是如何运作起来的呢?

例如输入n=10,第一次执行时执行else中的语句块(n为10的语句块被打断插入n为9的语句块),以此类推,语句块被不断插入,直到n=1。n=1执行if内的语句块,打印1后执行return函数,n=1的函数结束执行。然后程序就会执行被打断的printf语句,然后执行每一次的函数。

斐波那契数列

也可以用递归函数实现斐波那契数列,在利用递归解决这个函数之前,我们先用迭代的思想解决它,并且在最后对比这两种方法:

1迭代利用函数!

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
// 迭代函数计算斐波那契数列
void fibonacci(int n) {
    int a = 0, b = 1, c;
    for (int i = 0; i < n; i++) {
        printf("%d ", a);
        c = a + b;
        a = b;
        b = c;
    }
}

int main() {
    int n;
    // 输入要计算的斐波那契数列的项数
    printf("输入要计算的斐波那契数列的项数: ");
    scanf("%d", &n);
    // 输出斐波那契数列的前 n 项
    fibonacci(n);
    return 0;
}

2普通版(老老实实for循环)

代码语言:javascript
代码运行次数:0
运行
复制
#include<stdio.h>
int main(){
    int n=0;
    printf("输入你要打印的代码行数:");
    scanf("%d",&n);
    int f1,f2;
    for(int i=0;i<=n;i++){
        f1=f1+f2;//此处的f1为更新前的f1
        f2=f1+fn;
    }
    printf("%d %d",f1,f2);
    printf("\n");
return 0;
}

3递归函数实现

递归属于逆向思维,我们从最终想要的答案出发,逐步寻找上一层的答案,并且用他们构造当前层的答案。例如上图,我们要求得f(4)的值就要不断向上寻找。

代码语言:javascript
代码运行次数:0
运行
复制
#include<stdio.h>
//定义递归函数计算斐波那契数列
int fibonacci(int n){
    if(n<=1)
   {
      return n;//如果n为0或1,结果为自身
    }
    else
    { 
      return fibonacci(n-1)+fibonacci(n-2);
    }
  }

int main()
{
   int n=0;
   printf("输入你需要计算的斐波那契数列的项数:");
   scanf("%d",&n);
//输入要计算的斐波那契数列的项数
   for(int i=0;i<n;i++)
  {
      printf("%d",fibonacci(i);
  }
return 0;
}

迭代和递归对比

实现方式:

迭代: 通过循环结构,反复执行一组指令,直到满足特定条件为止。迭代通常使用循环语句(如for、while)来实现。 递归: 通过将问题分解为更小的、与原问题相似的子问题,并反复应用这个过程,直到达到基本情况(递归的终止条件)为止。

性能:

迭代: 通常比较直观,有时可能更高效,因为迭代往往不需要维护额外的函数调用栈递归: 可能会更简洁、易读,但有时可能导致函数调用栈溢出,尤其是对于大规模问题

空间复杂度:

迭代: 通常具有较低的空间复杂度,因为循环通常不需要额外的栈空间。 递归: 每次递归调用都需要在内存中维护一个函数调用栈,因此可能占用更多的内存空间。

汉诺塔思路讲解

我们要把第一个杆子以小的在上,大的在下的原则移走,想要把第三个圆盘移走,那么前两个就不能移动,所以我们得把前两个放置到B上。但是要想把前两个放置到B上,就得把最小的圆盘先移走,再把中等大小的圆盘放在B上,最后把最小的圆盘移回B,此时就可以移动最大的圆盘到C上。

C移动到B

A移动到C,B1移动到A

如果我们要移动的圆盘上没有别的圆盘,那么我们就可以直接对其移动,此时,我们生成三个概念:起始杆,中转杆和目标杆。

我们对于目标不同的盘子,转换的方法不同,那么我们在要生成的函数中定义一个坐标n,表示我们要移动的圆盘的数量,然后我们需要传入三个字符变量start,temp,end,分别对应起始杆,中转杆和结束杆。设置结束点为n=1,但是如果要转移的圆盘数目不止一个呢?我们就需要中转杆来实现目标。

代码实现

这个问题看似复杂,但我们将大问题不断分解,就可以划分为一个圆盘的小问题,而move函数正是负责移走n-1个圆盘,将n变为最上面一个圆盘(即n为1)的问题,而移走n-1个圆盘也正是不断分解为把目标圆盘变为最上面的圆盘移走。

当我们利用递归函数把n-1个函数都移动到中转杆上时,还需要再执行一次由起始杆到中转杆,再到目标杆的过程。

。以下是用C语言实现汉诺塔问题的递归代码:

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
// 函数原型
void hanoi(int n, char start, char temp, char target);
int main() {
    int n;
    // 输入汉诺塔的圆盘数量
    printf("Enter the number of disks: ");
    scanf("%d", &n);
    // 调用递归函数解决汉诺塔问题
    hanoi(n, 'A', 'B', 'C');
    return 0;
}

// 递归函数实现汉诺塔
void hanoi(int n, char strat, char temp, char target) {
    // 基本情况:只有一个圆盘
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", start, target);//如果只有一个圆盘可以直接从起始杆转移到目标杆
        return;
    }

    // 将n-1个圆盘从起始杆移动到中转杆
    hanoi(n - 1, start, target, temp);
    // 移动最大的圆盘到目标杆
    printf("Move disk %d from %c to %c\n", n, start, target);
    // 将n-1个圆盘从中转杆移动到目标杆
    hanoi(n - 1, temp, start, target);
}

写这篇博客前前后后花了三天时间,有些内容调试了很久,脑袋还有些晕乎。希望之后自己还能再消化消化,欢迎各位交流!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
<递归>汉诺塔 | 斐波那契数列 | 阶乘 (附python实现源码)汉诺塔问题求斐波那契数列背景故事:求阶乘
经典递归 汉诺塔问题 背景故事 传说印度某间寺院有三根柱子,上串64个金盘。寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕,世界就会灭亡。这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。但不知道是卢卡斯自创的这个传说,还是他受他人启发。 若传说属实,僧侣们需要 (2的64次方 − 1) 步才能完成这个任务;若他们每秒可完成一个盘子的移动,就需要5845亿年才能完成。整个宇宙现在也不过137亿年。 游戏规则: 1.借助B柱子将A柱子上面的圆盘
zhaoolee
2018/04/19
1.1K0
<递归>汉诺塔 | 斐波那契数列 | 阶乘 (附python实现源码)汉诺塔问题求斐波那契数列背景故事:求阶乘
手撕“汉诺塔算法”之详细图解
借助一个中转柱,使起始柱中按照规则排放的盘子移动到终点柱,且一次只能移动一个盘,且不允许大盘放在小盘上面,所以64个盘的移动次数是:18,446,744,073,709,551,615
灰小猿
2022/05/05
2K1
手撕“汉诺塔算法”之详细图解
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
递归其实是⼀种解决问题的⽅法,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰。 写⼀个史上最简单的C语⾔递归代码:
半截诗
2024/10/09
1540
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
汉诺塔问题(函数递归)
补充:汉诺塔问题挺经典的,以前我也一知半解,后来随着更深层次的学习,对递归的理解也要比之前更深,慢慢的就有了自己的理解,理解的重点就是在于递归参数的变换,其实就是原始杆和目标杆的寻找,原始杆就是带有盘子的杆子,目标杆就是我们打算往上挪动盘子的杆子
GG Bond1
2024/06/14
3070
C语言进阶指南(6)(函数递归详解)(内含汉诺比塔,青蛙跳台阶问题)
在数学中,递归是将一个未知项逐渐拆分为小项来计算出未知项的值。那么根据这种数学思想,递归程序的思路应该是:
代码小豪
2024/06/07
1510
递归经典题目
假设你在一个电影院,你想知道自己坐在哪一排,但是前面人很多,你懒得去数了,于是你问前一排的人「你坐在哪一排?」,这样前面的人 (代号 A) 回答你以后,你就知道自己在哪一排了——只要把 A 的答案加一,就是自己所在的排了。不料 A 比你还懒,他也不想数,于是他也问他前面的人 B「你坐在哪一排?」,这样 A 可以用和你一模一样的步骤知道自己所在的排。然后 B 也如法炮制。直到他们这一串人问到了最前面的一排,第一排的人告诉问问题的人「我在第一排」。最后大家就都知道自己在哪一排了。
BUG弄潮儿
2020/06/15
1.1K0
递归经典题目
《JavaSE-习题篇二》之七个题目,十六张图,让你不惧递归。
学习方法后,我们来学习一种特殊调用方法的方式,即递归。本篇文章将介绍什么是递归,以及递归的使用规则和注意事项,最后通过几道经典的题目来加深对递归的理解。
用户10517932
2023/10/07
2290
《JavaSE-习题篇二》之七个题目,十六张图,让你不惧递归。
关于我、重生到500年前凭借C语言改变世界科技vlog.9——青蛙跳台阶、汉诺塔
书说上回,讲到了函数递归,迭代,这章 vlog 将针对递归迭代解决十分经典的两个问题
DARLING Zero two
2024/11/19
1000
关于我、重生到500年前凭借C语言改变世界科技vlog.9——青蛙跳台阶、汉诺塔
递归-汉诺塔问题
汉诺塔传说:汉诺塔问题,是源于印度一个古老的益智玩具;大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
西湖醋鱼
2020/12/30
9020
递归-汉诺塔问题
C 语言函数递归探秘:从基础概念到复杂问题求解的进阶之路
递归可以理解为一种分而治之的思想:将复杂问题拆分为若干规模更小但相似的子问题,直到可以直接解决。
学无止尽5
2024/11/29
2220
C 语言函数递归探秘:从基础概念到复杂问题求解的进阶之路
递归求解汉诺塔问题
博主之前有写过关于递归问题的思维模式: 递归的思路 下面将用这种思维模式来求解经典汉诺塔问题。
VIBE
2022/12/02
4650
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
递归是一种在函数定义中直接或间接地调用自身的编程技巧。它就像是俄罗斯套娃,一个大娃娃里面套着一个小娃娃,小娃娃里面可能还套着更小的娃娃。在编程中,一个函数在执行过程中会调用自身来解决问题。
Rossy Yan
2025/01/13
1640
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
关于面试总结5-python笔试题(递归)
本篇继续收集一些常见的python笔试题,以基础知识为主,递归是面试最喜欢考的一个问题,不管是做开发还是测试,都无法避免考递归。本篇结合实际案例,讲下几种关于递归的场景。
上海-悠悠
2018/12/27
8780
关于面试总结5-python笔试题(递归)
算法回顾--如何写递归?
总之递归就是”装傻”的把原始的思路表现出来,不需要关心具体过程,只需要不停的缩小问题规模,然后答案自然就会被计算出来.
屈定
2018/09/27
7920
汉诺塔(问题以及扩展)
汉诺塔问题(三柱及四柱)详解 汉诺塔问题-步数 关于步数 是个很简单的问题 高中大家都学过 可能也做过类似的题
杨鹏伟
2020/09/11
1.2K0
从阶乘、斐波那契、汉诺塔剖析彻底搞懂递归算法
递归:就是函数自己调用自己。子问题须与原始问题为同样的事,或者更为简单; 递归通常可以简单的处理子问题,但是不一定是最好的。
bigsai
2019/09/24
5320
从阶乘、斐波那契、汉诺塔剖析彻底搞懂递归算法
读完这篇文章轻松理解递归算法
对于很多编程初学者来说,递归算法是学习语言的最大障碍之一。很多人也是半懂不懂,结果学到很深的境地也会因为自己基础不好,导致发展太慢。
C语言与CPP编程
2020/12/02
7350
读完这篇文章轻松理解递归算法
什么是递归,通过这篇文章,让你彻底搞懂递归
Beauty begins the moment you decide to be yourself.
好好学java
2020/10/27
8770
什么是递归,通过这篇文章,让你彻底搞懂递归
【C语言】函数递归例子1汉诺塔问题
汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
用户11290673
2024/09/25
1130
【C语言】函数递归例子1汉诺塔问题
对汉诺塔递归算法的简单理解
一.历史背景:汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
用户11305962
2024/10/09
3290
对汉诺塔递归算法的简单理解
推荐阅读
相关推荐
<递归>汉诺塔 | 斐波那契数列 | 阶乘 (附python实现源码)汉诺塔问题求斐波那契数列背景故事:求阶乘
更多 >
目录
  • 汉诺塔介绍
  • 递归知识点讲解
  • 斐波那契数列
    • 1迭代利用函数!
    • 2普通版(老老实实for循环)
    • 3递归函数实现
  • 迭代和递归对比
    • 实现方式:
    • 性能:
    • 空间复杂度:
  • 汉诺塔思路讲解
  • 代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验