递归算法实例应用(一)

简介: 递归算法实例应用(一):简单Hanoi 问题、N Queens问题。

递归算法实例应用(一)

递归简笔

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

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

斐波那契数列函数递归次序.png

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

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

​ 1)替代多重循环

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

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

​ ... ... ...


Hanoi (Simple)

Description

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

Input

汉诺塔内的盘子个数n(1 ≤ n ≤ 64)。

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 }. 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=1 j=2 j=3 j=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|均相等。

          如:主对角线2中,|1-2|=|2-3|=|3-4|=1

          ​ 主对角线3中,|2-1|=|3-2|=|4-3|=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均相等。

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

          ​ 副对角线3中,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层开始逐层递归解决
}
                          by Ss1Two 2023/01/12
目录
相关文章
|
6天前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
37 15
|
13天前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
3月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
103 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
1天前
|
人工智能 自然语言处理 供应链
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
|
8天前
|
存储 人工智能 算法
通过Milvus内置Sparse-BM25算法进行全文检索并将混合检索应用于RAG系统
阿里云向量检索服务Milvus 2.5版本在全文检索、关键词匹配以及混合检索(Hybrid Search)方面实现了显著的增强,在多模态检索、RAG等多场景中检索结果能够兼顾召回率与精确性。本文将详细介绍如何利用 Milvus 2.5 版本实现这些功能,并阐述其在RAG 应用的 Retrieve 阶段的最佳实践。
通过Milvus内置Sparse-BM25算法进行全文检索并将混合检索应用于RAG系统
|
14天前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
28 3
|
24天前
|
算法 Serverless 数据处理
从集思录可转债数据探秘:Python与C++实现的移动平均算法应用
本文探讨了如何利用移动平均算法分析集思录提供的可转债数据,帮助投资者把握价格趋势。通过Python和C++两种编程语言实现简单移动平均(SMA),展示了数据处理的具体方法。Python代码借助`pandas`库轻松计算5日SMA,而C++代码则通过高效的数据处理展示了SMA的计算过程。集思录平台提供了详尽且及时的可转债数据,助力投资者结合算法与社区讨论,做出更明智的投资决策。掌握这些工具和技术,有助于在复杂多变的金融市场中挖掘更多价值。
47 12
|
23天前
|
算法 安全 网络安全
基于 Python 的布隆过滤器算法在内网行为管理中的应用探究
在复杂多变的网络环境中,内网行为管理至关重要。本文介绍布隆过滤器(Bloom Filter),一种高效的空间节省型概率数据结构,用于判断元素是否存在于集合中。通过多个哈希函数映射到位数组,实现快速访问控制。Python代码示例展示了如何构建和使用布隆过滤器,有效提升企业内网安全性和资源管理效率。
50 9
|
2天前
|
人工智能 自然语言处理 算法
从第九批深度合成备案通过公示名单分析算法备案属地、行业及应用领域占比
2024年12月20日,中央网信办公布第九批深度合成算法名单。分析显示,教育、智能对话、医疗健康和图像生成为核心应用领域。文本生成占比最高(57.56%),涵盖智能客服、法律咨询等;图像/视频生成次之(27.32%),应用于广告设计、影视制作等。北京、广东、浙江等地技术集中度高,多模态融合成未来重点。垂直行业如医疗、教育、金融加速引入AI,提升效率与用户体验。
|
15天前
|
算法 安全 Java
探讨组合加密算法在IM中的应用
本文深入分析了即时通信(IM)系统中所面临的各种安全问题,综合利用对称加密算法(DES算法)、公开密钥算法(RSA算法)和Hash算法(MD5)的优点,探讨组合加密算法在即时通信中的应用。
17 0

热门文章

最新文章