递归算法实例应用(一)

news2025/1/17 5:57:29

递归算法实例应用(一)

递归简笔

递归和普通函数调用一样,都是通过函数栈实现。

以斐波那契数列递归调用为例
斐波那契数列递归调用图示

递归时函数调用栈的进栈、出栈过程可以由上述图示直观的体现出来,

因此可以得出递归的几个作用:

​ 1)替代多重循环

​ 2)解决以递归形式或潜在以递归形式定义的问题

​ 3)将问题规模分解,分解为规模更小的子问题进行求解

⋯ ⋯ ⋯ \cdots\cdots\cdots ⋯⋯⋯


Hanoi (Simple)

Description

古代有一座汉诺塔,塔内有3个座A、B、C,A座上有n个盘子,盘子大小不等,大的在下,小的在上,如图4-1所示。有一个和尚想把这n个盘子从A座移到C座,但每次只能移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下,小盘在上。在移动过程中可以利用B座来放盘子。要求输出移动的步骤。

Input

汉诺塔内的盘子个数 n ( 1 ≤ n ≤ 64 ) n(1 \le n \le 64) n1n64

Output

输出移动的步骤,每行一步,如从A座移动到C座,输出“A->C”。

Sample Input

3

Sample Output

A->C
A->B
C->B
A->C
B->A
B->C
A->C

这是最简单的Hanoi问题,其余进阶版的Hanoi问题均由此发展而来。

该题是一个非常明显的以递归形式定义的问题,即:

要把A座上的盘子移动到C座的问题分解为三步:

  1. 把A座上的n-1个盘子移动到B座作为中转,且以C座作为中转;
  2. 把A座剩余的一个盘子移动到C座;
  3. 再把B座的n-1个盘子移动到C座,且以A做作为中转。

这样就把原问题的规模减小至n-1,通过逐层递归可以将问题规模减少至1,此时就可以通过直接将A座盘子移动到C座完成,在逐步向上返回至上一层调用函数,直至问题规模n完成求解。

由上述分析显然可知,当问题规模减少至1时为终止条件,此时应将A座盘子移动到C座上,即输出A->C,同时结束本次递归,返回至上一层递归函数处。

所以主函数及递归段代码为:

void Hanoi(int n, char A, char B, char C) {
    if (n == 1) {//问题规模到达1时为递归终止条件
        printf("%c->%c\n", A, C);
        return;
    }
    //将A座n-1个盘子移动至B座,同时以C座作为中转
    Hanoi(n - 1, A, C, B);
    //将A座第n个盘子移动至C座
    printf("%c->%c\n", A, C);
    //将B座的n-1个盘子移动至C座,同时以A座作为中转
    Hanoi(n - 1, B, A, C);
}

int main() {
    int n;
    scanf("%d",&n);
    //将n层汉诺塔中A座移动到C座,同时以B座作为中转
    Hanoi(n, 'A', 'B', 'C');
}

N Queens

Description

The eight queens puzzle is the problem of putting eight chess queens on an 8 × 8 chessboard such that none of them is able to capture any other. The puzzle has been generalized to arbitrary n × n boards. Given n, you are to find a solution to the n queens puzzle.

Input

The input contains multiple test cases. Each test case consists of a single integer n between 8 and 300 (inclusive). A zero indicates the end of input.

Output

For each test case, output your solution on one line. The solution is a permutation of { 1 , 2 , ⋯   , n } \{1,2,\cdots,n \} {1,2,,n}. The number in the ith place means the ith-column queen in placed in the row with that number.

Sample Input

4

Sample Output

2 4 1 3
3 1 4 2

为方便理解题意,依样例输入输出可以画出四皇后图例:

img

题中要求皇后不能在同一行、同一列、同一对角线。

不难想到可以通过枚举所有可能的皇后状态来暴力求解,比如当 N = 4 时,可以通过4重循环嵌套来实现枚举。

但是该问题的问题规模N是一个不确定是整数,N重循环嵌套就无法实现,所以可以考虑采用递归来替代多重循环。

分析至此,本题的递归又自然而然的引出了回溯法这一基本算法理念,当不断的枚举每一种可能的状态时,若发生了与规则矛盾的错误,则应立即终止本层递归,回溯至上层,进行下一状态的枚举。

准备工作:

而如何枚举全每一种可能的状态,有多种方法,如按行枚举、按列枚举、按对角线枚举等,本文采用按行枚举进行操作。所以递归函数的形参就呼之欲出:int n,因为我们假定递归模型是按行进行枚举的,所以n用于表示0~n-1行皇后已经摆好,当前递归函数为处理第n行皇后的位置。

所以应再设置一个PosOfQueen的一维数组,其下标表示棋盘的第i行,其值PosOfQueen[i]表示第i行皇后摆放在第几列,如PosOfQueen[2]=4,表明第2行(棋盘的第3行)皇后摆放第4列(棋盘的第五列)。


算法逻辑:

其中,NofQueen表示N皇后中的规模N

  1. 若本层递归参数n==NofQueen,即表明N皇后已经摆放正确,所以此时为该问题的递归终止条件,应输出问题的一个解。

    代码描述为:

    if (n == NofQueen) {//N个皇后已经摆好
        for (i = 0; i < NofQueen; ++i) {
            printf("%d ", PosOfQueen[i] + 1);//输出每一行皇后摆在哪一列
        }
        printf("\n");
        return;
    }
    
  2. 若本层递归不为第N行,则应处理第n行的皇后,即确定第n行皇后的摆放位置。所以应从0~NofQueen-1枚举所有可能的摆放位置。假设以i枚举时行递增变量

    枚举时,首先,与前0~n-1行已经确定位置的n个元素进行比较,判断是否同行、同列、同对角线

    • 同行判定:由于是按行遍历每行确定一个元素的位置,所以不可能有元素是同行。

    • 同列判定:PosOfQueen[j]数组的值表明第j行皇后摆放在第几列,所以同列判断较为简单,代码描述为:

      PosOfQueen[j] == i
      
    • 同对角线判定:

      j=1j=2j=3j=4
      i=1(1,1)(1,2)(1,3)(1,4)
      i=2(2,1)(2,2)(2,3)(2,4)
      i=3(3,1)(3,2)(3,3)(3,4)
      i=4(4,1)(4,2)(4,3)(4,4)

      上表描述为4*4的棋盘,其中

      1. 主对角线

        • 主对角线1:(1,1)、(2,2)、(3,3)、(4,4)

        • 主对角线2:(1,2)、(2,3)、(3,4)

        • 主对角线3:(2,1)、(3,2)、(4,3)

        • 主对角线4:(1,3)、(2,4)

        • 主对角线x: …、…

          观察发现,每一个主对角线上各个元素的行列下标差的绝对值均相等,即 ∣ i − j ∣ |i-j| ij均相等。

          如:主对角线2中, ∣ 1 − 2 ∣ = ∣ 2 − 3 ∣ = ∣ 3 − 4 ∣ = 1 |1-2|=|2-3|=|3-4|=1 ∣12∣=∣23∣=∣34∣=1

          ​ 主对角线3中, ∣ 2 − 1 ∣ = ∣ 3 − 2 ∣ = ∣ 4 − 3 ∣ = 1 |2-1|=|3-2|=|4-3|=1 ∣21∣=∣32∣=∣43∣=1

      2. 副对角线

        • 副对角线1:(1,4)、(2,3)、(3,2)、(4,1)

        • 副对角线2:(1,3)、(2,2)、(3,1)

        • 副对角线3:(2,4)、(3,3)、(4,2)

        • 副对角线4:(1,2)、(2,1)

        • 副对角线x: …、…

          观察发现,每一个副对角线上各个元素的行列下标之和均相等。即 i + j i+j i+j均相等。

          如:副对角线2中, 1 + 3 = 2 + 2 = 3 + 1 = 4 1+3=2+2=3+1=4 1+3=2+2=3+1=4

          ​ 副对角线3中, 2 + 4 = 3 + 3 = 4 + 2 = 6 2+4=3+3=4+2=6 2+4=3+3=4+2=6

    综上所述,可将同行(忽略)、同列、同主副对角线判定代码合并为:

    if (PosOfQueen[j] == i || abs(PosOfQueen[j] - i) == abs(n - j)) {
        //同行不必判断,判断是否同列、同主对角线、同副对角线。
        break;//规则冲突,测试下一可能位置,再通过剪枝来减少不必要的开销
    }
    

    其次,若与前0~n-1行判断是否同行、同列、同对角线完成后,与题目规则不冲突,则将第n行皇后摆放在当前枚举状态下的的可能位置i处,随后进行下一行皇后位置的确定,即进入下一层递归中。

    代码描述如下:

    if (j == n) {//当前轮次枚举中位置i与前0~n-1行合法性判断均通过,当前位置合法
        PosOfQueen[n] = i;//将第n个皇后摆放在位置i
        NQueen(n + 1);//递归调用函数,处理n+1行皇后位置摆放问题
    }
    
  3. 至此,递归函数已经完成,在递归函数中,第一步中的if语句为递归终止点,同时也是回溯法成功找到解时的判定条件。在第二步中,通过逐步尝试每一个可能位置的,并通过回溯保证本次递归是在上一层递归函数正确的条件下进行的枚举尝试,不断穷尽解集二叉树中的所有分支。


代码整合:

综上,代码可整合为:

void NQueen(int n) {//在0~n-1行的皇后已经摆好的情况下,摆第n行及其之后的皇后
    int i, j;
    if (n == NofQueen) {//N个皇后已经摆好
        for (i = 0; i < NofQueen; ++i) {
            printf("%d ", PosOfQueen[i] + 1);//输出每一行皇后摆在哪一列
        }
        printf("\n");
        return;
    }
    for (i = 0; i < NofQueen; ++i) {
        for (j = 0; j < n; ++j) {//每一轮for循环均回溯至i行处,因此可以穷尽所有可能解的二叉树的叶子结点
            if (PosOfQueen[j] == i || abs(PosOfQueen[j] - i) == abs(n - j)) {
                //同行不必判断,判断是否同列、同主对角线、同副对角线。
                break;//规则冲突,test next pos  再通过剪枝来减少不必要的开销
            }
        }
        if (j == n) {//与前0~n-1行合法性判断均通过,当前位置合法
            PosOfQueen[n] = i;//将第n个皇后摆放在位置i
            NQueen(n + 1);//递归调用函数,处理n+1行皇后位置摆放问题
        }
    }
}

int NofQueen;           //N皇后的N
int PosOfQueen[100];    //棋盘数组,下标表示行号,值表示列号

int main() {
    scanf("%d", &NofQueen);
    NQueen(0);//从棋盘第0层开始逐层递归解决
}

b y ⋯ S s 1 T w o ⋯ 2023 / 01 / 12 by\cdots Ss1Two\cdots2023/01/12 bySs1Two2023/01/12

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

玩转CodeQLpy之代码审计实战案例

0x01 背景介绍CodeQLpy是一款半自动化的代码审计工具&#xff0c;能有效提高代码审计的效率&#xff0c;目前项目仍处于测试阶段。项目地址https://github.com/webraybtl/CodeQLpy&#xff0c;在github主页有对应的安装和使用介绍&#xff0c;如图1.1所示。-t: 指定待扫描的源码…

SignalR在Asp.NetCore的使用入门

SignalR在ASP.NET CORE下的使用 SignalR是微软提供的实时Web通信的库&#xff0c; 它会自己根据情况使用不同的链接方式&#xff0c;主要有 WebSocketlong pollingForever frameSever Events Sent 等方式&#xff0c;故可以不管其具体的实现方式&#xff0c;差不多等于开箱…

Docker部署 Oracle12c

Docker部署 Oracle12c Docker部署 registry Docker搭建 svn Docker部署 Harbor Docker 部署SQL Server 2017 Docker 安装 MS SqlServer Docker部署 Oracle12c 文章目录Docker部署 Oracle12c部署步骤1.查询oracle镜像2.下载镜像3.创建容器4.进入容器5.连接oracle数据库部署步骤…

Unreal动态多播委托

相对于普通多播委托,动态多播委托可以在蓝图中绑定事件。在这里记录一下它的使用方式:定义动态多播委托类型:声明委托变量:绑定函数:通过Add绑定:需要定义FScriptDelegate变量:为FScriptDelegate添加绑定函数这种方法的好处是可以针对单个函数进行解绑:通过AddDynamic标记宏绑定…

Linux 调度器之抢占式调度

文章目录前言一、简介1.1 need_resched标志1.2 try_to_wake_up二、抢占调度2.1 抢占简介2.2 用户态抢占2.2.1 从系统调用返回用户空间2.2.2 从中断返回用户空间2.3 内核态抢占2.2.1 preempt_count2.2.2 调用preempt_enable()2.2.3 从中断返回内核空间时总结参考资料前言 在这片…

worker多线程的使用与理解

JavaScript是单线程的&#xff0c;并不是说它是单线程语言&#xff0c;只能说在浏览器中运行是单线程的&#xff0c;单线程会免去许多麻烦&#xff0c;比如说&#xff0c;有两个线程同时进行DOM操作&#xff0c;一个是在父级下添加子元素&#xff0c;一个是删除这个父级元素&am…

Pandas数据清洗函数大全

文章目录 一、drop()&#xff1a;删除指定行列 1. 删除指定行2. 删除指定列 二、del()&#xff1a;删除指定列三、isnull()&#xff1a;判断是否为缺失 1. 判断是否为缺失2. 判断哪些列存在缺失3. 统计缺失个数 四、notnull()&#xff1a;判断是否不为缺失五、dropna()&#x…

Java笔记024-Math类、Arrays类、System类、BigInteger和BigDecimal类、日期类

常用类Math类基本介绍Math类包含用于执行基本数学运算的方法&#xff0c;如初等指数、对数、平方根和三角函数方法一览(均为静态方法)Math常见方法应用案例1、 abs绝对值2、pow求幂3、ceil向上取整4、floor 向下取整5、round 四舍五入6、sqrt 求开方7、random求随机数//思考&am…

Webpack常见的插件和模式

1、认识Plugin Webpack的另一个核心是Plugin&#xff0c;官方有这样一段对Plugin的描述&#xff1a; While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset manageme…

《对线面试官》| 高频计算机网络面试题 pt.2

目录11、Get与POST的区别12、Session、Cookie 的区别13、简单聊聊 HTTP 协议吧14、URI 和 URL 的区别15、GET 和 POST 方法都是安全和幂等的吗&#xff1f;16、说说 HTTP/1.1 相比 HTTP/1.0 提高了什么性能&#xff1f;17、那上面的 HTTP/1.1 的性能瓶颈&#xff0c;HTTP/2 做了…

中国蚁剑的工作原理

中国蚁剑连接http://192.168.11.157/dvwa/hackable/uploads/pass.php蚁剑连接并同时用wireshark抓取流量1274 行&#xff0c;追踪tcp流因为我们的php.php内容是 $_POST[pass]&#xff0c;所以这里是post了一个pass参数&#xff0c;后面跟上了命令。通过站长工具-URL解码/编码 (…

15.Isaac教程--Isaac机器人引擎简介

Isaac机器人引擎简介 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录Isaac机器人引擎简介基础Codelets完整的应用基础 本节介绍如何使用 Isaac 机器人引擎。 它介绍了相关术语并解释了 Isaac 应用程序的结构。 Isaac 应用程序由 JavaS…

卫星AIS接收机

1.设备简介星载AIS模块&#xff0c;专门针对小卫星设计的AIS载荷&#xff0c;设计时考虑到CubeSat的尺寸、重量和功率限制&#xff0c;也可以作为较大的LEO卫星上的有效载荷。2.产品特征独立4信道AIS接收机集成LNA和SAW滤波器AIS帧的数据存储支持频谱样本采集安全在轨软件升级支…

【Wiki】XWiki安装教程_War包版本

目录0、XWiki说明1、war包安装说明1.1、环境说明1.2、如果懒得下载可以使用这边准备好的物料包汇总2、war包安装2.1、Tomcat安装2.2、java安装(需要root权限)2.3 、使用 source /etc/profile 刷新linux配置2.4、数据库安装2.5、解压war包与xip2.6、修改配置文件2.6.1、修改WEB-…

mysql快速生成100W条测试数据(4)全球各城市房价和销售数据并存入mysql数据库

首先这个就是我们需要生成的数据类型&#xff0c;这种只是我们用于测试以及学习时候使用&#xff0c;主要就是全球城市房价的均值和一些虚拟的销售数据 这是之前的文章里面包含一些以前的一些操作流程可以进行参考学习 更加详细操作步骤在第一篇文章里面 mysql快速生成100W条测…

Speckle Revit连接器使用教程

Speckle Revit 连接器目前支持 Autodesk Revit 2020、2021、2022 和 &#x1f195;2023。 1、安装Speckle revit连接器 要安装 Revit 连接器并添加 Speckle 帐户&#xff0c;请按照 Speckle 管理器中的说明进行操作。 安装后&#xff0c;可以在Speckle选项卡下的功能区菜单中…

一个前端大神电脑里的秘密

前言作为前端仔&#xff0c;当你入职一家公司&#xff0c;拿到新发的电脑&#xff0c;你会对电脑干点啥&#xff0c;装开发环境&#xff1f;装软件&#xff1f;你是否铺天盖地到处找之前电脑备份的东西&#xff1f;又或者是想不起来有什么上一台电脑好用的软件叫什么名&#xf…

KT148A语音芯片420s秒的语音空间是什么意思,mp3文件支持多大

一、问题简介 我想问一下KT148A这个芯片真的能存420秒的语音么&#xff1f;我随便一个5秒的语音mp3格式都65k了&#xff0c;如果是这样的话 那我的mp3的源文件在最小的采样率和最小码率的情况下 mp3文件可以支持多大&#xff1f;有没有实际测试的数据&#xff0c;使用的是一线串…

【可解释性机器学习】可解释机器学习简介与特征选择方法

特征选择&#xff1a;Feature Importance、Permutation Importance、SHAP1. Introduction什么是可解释机器学习&#xff08;Explainable ML&#xff09;&#xff1f;为什么需要Explainable ML?直接使用一些可以interpretable的模型不好吗&#xff1f;2. Local Explanation方法…

Homekit智能家居DIY-智能吸顶灯

灯要看什么因素 照度 可以简单理解为清晰度&#xff0c;复杂点套公式来说照度光通量&#xff08;亮度&#xff09;单位面积&#xff0c;简单理解的话就是越靠近灯光&#xff0c;看的就越清楚&#xff0c;是个常识性问题。 不同房间户型对照度的要求自然不尽相同&#xff0c;…