递归(二)—— 初识暴力递归

  • 如何理解暴力递归?

字面意思就是——“暴力的递归”,就是——“别纠结细节,开整(递归)!”

暴力递归就是尝试。即:只要满足递归条件就不断的递归下去,直到达到base case,结束递归。

  • 暴力递归的思想

1. 把问题转化为规模缩小了的同类问题的子问题;

2. 有明确的不需要继续进行递归的条件(也就是base case);

3. 有当得到了子问题的结果之后的决策过程;

4. 不记录每一个子问题的解;

  • 题目

题目1: 汉诺塔问题

汉诺塔(焚天塔)问题是一个经典的数学谜题,涉及到三个柱子和一系列不同大小的盘子。最初,所有盘子都按照从小到大的顺序叠放在一个柱子上。最大的盘子在最下方。目标是将这些盘子移动到另一个柱子上,同时保持它们的原有顺序,并且在任何时候都不能将较大的盘子放在较小的盘子之上。

题目分析

汉诺塔是典型的利用递归思想解决的问题。下面我们给出具体的数字,通过画图来分析问题。假设当前的汉诺塔是3层,叠放在最左边的柱子上,目标是大小顺序不变的叠放在最右边的柱子上。

如果我们手边就有这样一套汉诺塔玩具,手动的来操作一下,怎么做呢?

第一步:将1号放在右侧;

第二步:将2号放在中间;

第三步:将1号放在中间;

第四步:将3号放在右侧;

第五步:将1号放在左侧;

第六步:将2号放在右侧;

第七步:将1号放在右侧

经过实际操作可是,如果有3个盘子,那么需要7步就可以满足题目要求,可是如果是未知的N个盘子呢?为了实现这个目的,我们按照“把大象放冰箱”的逻辑,只需三步即可:

第一步,将1~n-1号盘子看作整体,移到辅助柱子(中间柱子)上;

第二步,将N号盘子移到最右侧柱子;

第三步,将被移到辅助柱子上1~n-1号盘子再移到最右侧柱子。

这个思路的代码实现就是代码段1.

代码实现

//代码段1
void leftToRight(int n) {
    if (n == 1) {
        printf("move 1 from left to right\n");
        return;
    }

    leftToMid(n - 1);
    printf("move %d from left to right\n", n);
    midToRight(n - 1);
}

代码分析:

函数leftToRight 就是按照冰箱三步法的思路写的,当n == 1时,表示只有一个盘子,不用纠结,直接放到目的地最右侧即可,当n>1 时,将1~n-1的盘子放到中间柱子,即函数leftToMid, 然后将第n个盘子放在最右侧,最后将中间柱子上的盘子放在最右侧,函数midToRight。

函数leftToRight 的逻辑很容易理解,接下来完成 leftToMid 和midToRight 的函数逻辑。

void leftToMid(int n):

这个函数的作用是将盘子从左侧移到中间,也就是冰箱逻辑的的第一步(“第一步,将1~n-1号盘子看作整体,移到辅助柱子(中间柱子)上”),但是这个移动过程的思维逻辑又是一个冰箱三步法,即:

第一步:将左侧的盘子1~{n}'-1 的部分先移动到辅助柱子,此时的辅助柱子是右侧柱子,

第二步:将第{n}'个盘子移动中间;

第三步:将1~{n}'-1再移到中间柱子,需要函数 rightToMid。

void midToRight(int n):

同样的逻辑,第一步将1~{n}'-1 移到辅助柱子,此时的辅助柱子是左侧柱子,此时需要midToLeft函数,第二步,将第{n}'个盘子从中间移到右侧,第三步将1~{n}'-1个盘子从左侧移到右侧

//代码段2
void leftToMid(int n) {
    if (n == 1) {
        printf("move 1 form left to mid\n");
        return;
    }
    leftToRight(n - 1);
    printf("move %d form left to mid\n", n);
    rightToMid(n - 1);
}

void midToRight(int n) {
    if (n == 1) {
        printf("move 1 form mid to right\n");
        return;
    }
    midToLeft(n - 1);
    printf("move %d from mid to right\n",n);
    leftToRight(n-1);
}

rightToMid 函数和midToLeft函数还没有定义,按照相同的思路,完成代码。

//代码段3
void rightToMid(int n) {
    if (n == 1) {
        printf("move 1 form right to mid\n");
        return;
    }
    rightToLeft(n - 1);
    printf("move %d form rihgt to mid\n", n);
    leftToMid(n - 1);
}

void midToLeft(int n) {
    if (n == 1) {
        printf("move 1 form mid to left\n");
        return;
    }
    midToRight(n - 1);
    printf("move %d from mid to left\n", n);
    rightToLeft(n-1);
}

void rightToLeft(int n) {
    if (n == 1) {
        printf("move 1 frome right to left\n");
        return;
    }
    rightToMid(n - 1);
    printf("move %d from right to left\n",n);
    midToLeft(n - 1);
}

写完所有的调用过程,发现每一个子过程都要调用另外两个子过程,循环嵌套,将完整的函数调用补充完成,运行程序。

#include <stdio.h>

void leftToRight(int n);
void leftToMid(int n);
void rightToLeft(int n);
void rightToMid(int n);
void midToLeft(int n);
void midToRight(int n);

void hanoi_1(int n){
    leftToRight(n);
}

int main(){
    hanoi_1(3);
    return 0;
}

 运行结果

上图的运行结果与之前我们手动操作的结果是一致的。

代码优化

我们用了6个子过程完成了题目要求,但这么写代码的弊端是显而易见的,就是过于冗余复杂,其实6个子函数的逻辑是一样的,每个子过程都涉及三个柱子,即当前柱子,目的柱子和辅助柱子,只不过在6个子过程中这三个柱子所代表的柱子标号不一样。但是逻辑是一样的;

第一步:1~n-1从当前柱子到辅助柱子

第二步:n从当前柱子到目的柱子

第三步:1~n-1从辅助柱子到目的柱子

#include <stdio.h>

void hanoi_2(int n, const char* from, const char* to, const char* other){
    if(n == 1){
        printf("move 1 from %s to %s\n", from, to);
        return;
    }
    hanoi_2(n-1, from, other, to);
    printf("move %d from &s to %s\n", n, from, other);
    hanoi_2(n-1, other, to, from);
}

int main(){
    printf("call hanoi_2\n");
    hanoi_2(3,"left","right","mid");
}

运行结果:

总结

hanoi_1的6个子过程的分析思路是必要的,它为hanoi_2 这个优化函数打下了逻辑基础。我们学到了一个优化技巧:一个递归函数我们可以用增加参数的方式表达更多的可能性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/767096.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣习题--哈沙德数

一、前言 本系列主要讲解和分析力扣习题&#xff0c;所以的习题均来自于力扣官网题库 - 力扣 (LeetCode) 全球极客挚爱的技术成长平台 二、哈沙德数 1. 哈沙德数 如果一个整数能够被其各个数位上的数字之和整除&#xff0c;则称之为 哈沙德数&#xff08;Harshad number&…

LeetCode刷题之HOT100之除自身以外数组的乘积

2024 7/3 今天天气依旧很好&#xff0c;想起来做一题。 1、题目描述 2、算法分析 给定一个数组&#xff0c;要返回初自身以外数组的乘积。咋做呢&#xff1f;是的&#xff0c;我只能想到暴力解法&#xff0c;这不符合时间复杂度O(n)的要求&#xff0c;所以我只能看一下题解了…

零一万物: Yi Model API的使用

一、获取API Key 通过官方网址注册账号并且认证: 零一万物大模型开放平台 创建API Key 二、安装及调用 安装OpenAI SDK ​ 零一万物API 接口兼容 OpenAI 的 Python SDK&#xff0c;只需要简单配置即可使用。 安装 OpenAI SDK。请确保使用的 Python 版本至少为 3.7.1&a…

检索生成(RAG) vs 长文本大模型:实际应用中如何选择?

编者按&#xff1a;大模型的上下文理解能力直接影响到 LLMs 在复杂任务和长对话中的表现。本期内容聚焦于两种主流技术&#xff1a;长上下文(Large Context Windows)和检索增强生成(RAG)。这两种技术各有何优势&#xff1f;在实际应用中&#xff0c;我们又该如何权衡选择&#…

数据质量管理-可访问性管理

前情提要 根据GB/T 36344-2018《信息技术 数据质量评价指标》的标准文档&#xff0c;当前数据质量评价指标框架中包含6评价指标&#xff0c;在实际的数据治理过程中&#xff0c;存在一个关联性指标。7个指标中存在4个定性指标&#xff0c;3个定量指标&#xff1b; 定性指标&am…

【Windows】draw.io(免费的开源跨平台绘图软件)软件介绍

软件介绍 draw.io 是一款免费且易于使用的在线流程图绘图软件&#xff0c;后来更名为 diagrams.net。它最初作为一个基于 Web 的应用程序提供&#xff0c;支持用户创建各种类型的图表、流程图、网络图、组织结构图、UML 图等。它是完全免费的、强大的、专业的、易于使用的和高…

C++使用Poco库封装一个HTTP客户端类--Query参数

0x00 概述 我们使用Poco库的 Poco::Net::HTMLForm 类可以轻松实现表单数据的提交。 0x01 ApiPost提交表单数据 0x02 HttpClient类 #ifndef HTTPCLIENT_H #define HTTPCLIENT_H#include <string> #include <map> #include <Poco/URI.h> #include <Poco/N…

引领视觉基础模型新纪元! | 微软宣布开源Florence-2

01 模型介绍 &#x1f389;重大突破&#xff01;微软宣布开源Florence-2视觉基础模型&#xff0c;引领AI新纪元&#xff01;&#x1f680; Florence-2这一创新力作&#xff0c;以统一的提示为基础&#xff0c;跨越式地解决了计算机视觉与视觉语言领域的多样任务难题。从字幕生…

Hyper-V虚拟机固定IP地址(手把手教设置)

链接虚拟机修改网络配置文件 输入指令 sudo vi /etc/sysconfig/network-scripts/ifcfg-eth0 然后 输入 按 i 键 再按回车 (enter) 进入编辑模式 修改配置(这几项)其中 IPADDR 就是你想给虚拟机固定的 IP 地址 多台的话只需要修改这个IP 就行其他不变 BOOTPROTO=static…

半导体划片研磨废水的处理效果

半导体划片研磨废水处理是一个复杂而关键的过程&#xff0c;因为这类废水中含有大量颗粒物、有机物、重金属等有害物质&#xff0c;具有浓度高、毒性大、难以处理等特点。以下是对半导体划片研磨废水处理过程的详细阐述&#xff0c;结合相关数字和信息进行归纳&#xff1a; 一、…

【Java集合类】ArrayList

方法 subList(int fromIndex, int toIndex) 可以看一下subList源码片段 public List<E> subList(int fromIndex, int toIndex) {subListRangeCheck(fromIndex, toIndex, size);return new SubList<>(this, fromIndex, toIndex);} private static class SubList…

nginx的vim nginx.conf配置文件内容详解及实验,nginx的优化和防盗链

一、nginx网络服务器&#xff1a; 1. nginx是开源的&#xff0c;是一款高性能&#xff0c;轻量级的web服务软件&#xff1b;稳定性高&#xff0c;而且版本迭代比较快&#xff1b;修复bug速度比较快&#xff0c;安全性高&#xff1b;消耗资源低&#xff0c;http的请求并发连接&…

My sql 安装,环境搭建

以下以MySQL 8.0.36为例。 一、下载软件 1.下载地址官网&#xff1a;https://www.mysql.com 2. 打开官网&#xff0c;点击DOWNLOADS 然后&#xff0c;点击 MySQL Community(GPL) Downloads 3. 点击 MySQL Community Server 4.点击Archives选择合适版本 5.选择后下载第二个…

bWAPP靶场安装

bWAPP安装 下载 git地址&#xff1a;https://github.com/raesene/bWAPP 百度网盘地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1Y-LvHxyW7SozGFtHoc9PKA 提取码&#xff1a;4tt8 –来自百度网盘超级会员V5的分享 phpstudy中打开根目录&#xff0c;并将下载的文…

【C++知识点总结全系列 (06)】:STL六大组件详细总结与分析- 配置器、容器、迭代器、适配器、算法和仿函数

STL六大组件目录 前言1、配置器(1)What(2)Why(3)HowA.调用new和delete实现内存分配与销毁B.STL Allocator (4)allocator类A.WhatB.HowC.allocator的算法 2、容器(1)What(2)Which&#xff08;有哪些容器&#xff09;(3)序列容器&#xff08;顺序容器&#xff09;A.WhichB.array&…

Unity编辑器工具---版本控制与自动化打包工具

Unity - 特殊文件夹【作用与是否会被打包到build中】 Unity编辑器工具—版本控制与自动化打包工具&#xff1a; 面板显示&#xff1a;工具包含一个面板&#xff0c;用于展示软件的不同版本信息。版本信息&#xff1a;面板上显示主版本号、当前版本号和子版本号。版本控制功能…

音视频开发35 FFmpeg 编码- 将YUV 和 pcm合成一个mp4文件

一 程序的目的 /*** *该程序的目的是: * 将 一个pcm文件 和 一个 yuv文件&#xff0c;合成为一个 0804_out.mp4文件 * pcm文件和yuv文件是从哪里来的呢&#xff1f;是从 sound_in_sync_test.mp4 文件中&#xff0c;使用ffmpeg命令 抽取出来的。 * 这样做的目的是为了对比前…

【C语言】文件的顺序读写

©作者:末央&#xff06; ©系列:C语言初阶(适合小白入门) ©说明:以凡人之笔墨&#xff0c;书写未来之大梦 目录 前言字符输入输出函数 - fgetc和fputc文本行输入输出函数 - fgets和fputs格式化输入输出函数 - fscanf和fprintf 前言 对文件数据的读写可以分为顺序…

【Elasticsearch】一、概述,安装

文章目录 概述全文搜索引擎概述ES&#xff08;7.x&#xff09; 安装ES&#xff08;Docker&#xff09;测试&#xff0c;是否启动成功 可视化工具配置中文 客户端Postman下载 概述 ES是开源的高扩展的分布式全文搜索引擎&#xff0c;实时的存储、检索数据&#xff1b;本身扩展性…

function-calling初体验

课程地址&#xff1a;https://learn.deeplearning.ai/courses/function-calling-and-data-extraction-with-llms/lesson/1/introduction github notebook地址&#xff1a;https://github.com/kingglory/LLMs-function-calling/tree/main Function-Calling 介绍 函数调用(Funct…