14-C++基本算法-深度优先搜索

news2024/11/18 3:31:09

📚 从递归阶乘到深度优先搜索

在学习深度优先搜索之前,我们先回顾一下递归阶乘的实现。递归阶乘是一种典型的递归算法,它通过将问题分解为更小的子问题来解决。

#include <iostream>
using namespace std;

int factorial(int n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

int main() {
    int n = 5;
    cout << n << "的阶乘为:" << factorial(n) << endl;
    return 0;
}

这样对于factorial(5),它的求解路线为

计算factorial5
计算factorial4
计算factorial3
计算factorial2
计算factorial1

递归求解5的阶乘是以factorial(5)为起点,然后按照特定路线一路执行到终点factorial(1)结束的,这中间还经历了factorial(4),factorial(3),factorial(2)几个中间状态。

上述代码通过递归调用factorial函数来计算阶乘,我们可以将其视为一种深度优先搜索的实现方式。在每一次递归调用中,我们都深入到更小的子问题中,直到达到终止条件,然后回溯返回结果。

深度优先搜索是也从某个起点出发,经过若干个中间状态达到某个可能的终点状态。

但它还是带着搜索任务的算法,即在抵达终点前的每个状态上都可能面临着若干个选择,并且每次不同的选择往往还会影响到后面的选择结果,包括探索失败。

例如,假设你身处一座迷宫塔之中,每层塔都有固定的若干个通往下一层的入口,当选择其中一个入口下去时有可能成功进入到下一层大厅,也有可能发现是个死胡同,只能返回到上一层重新选择入口。

令f(n)表示当前身处塔的第n层,则从5楼开始探索下楼的路线时:从f(5)到f(4),从f(4)到f(3),从f(3)到f(2)…都面临着若干种入口选择。

运气好我们既有可能一次性完成f(5)->f(4)->f(3)->f(2)->f(1),也有可能f(5)->f(4)->f(3)(此路不通)->f(4)(回到4楼重新选择)->…

深度优先搜索(DFS)模式,其基本思想是:

先选择某一种可能的情况向前搜索,直到无路可走,再退回到上一个状态,探寻其它的方向可能。

如此循环往复,直到得到题解或证明无解。

这一过程也可称之搜索与回溯

两个动图更好地理解深度优先搜索(搜索与回溯)
在这里插入图片描述

在这里插入图片描述

🗝️ 密码锁的所有密码组合

现在,我们来解决一个实际问题:列举一个3位密码锁的所有可能的密码组合。密码锁的每一位数字可以选择0~9之间的任意一个数字。

💡 思路解析:

  • 定义一个长度为3的数组digits,用于存储密码锁的每一位数字。
  • 使用一个递归函数dfs来遍历密码锁的所有可能的密码组合。
  • 在递归函数中,通过循环遍历0~9之间的数字,依次将每个数字赋值给当前位的密码。
  • 如果遍历到了最后一位密码,即第3位,将当前的密码组合输出。
  • 否则,递归调用dfs函数,深入到下一位密码的选择中。
#include <iostream>
using namespace std;

void dfs(int depth, int digits[]) {
    if (depth == 3) {
        cout << digits[0] << digits[1] << digits[2] << endl;
        return;
    }
    for (int i = 0; i <= 9; i++) {
        digits[depth] = i;
        dfs(depth + 1, digits);
    }
}

int main() {
    int digits[3] = {0};
    dfs(0, digits);
    return 0;
}

通过以上代码,我们可以得到密码锁的所有可能的密码组合。这个例子展示了深度优先搜索的一种典型应用方式,通过递归和回溯来遍历问题的所有可能解。

🔄 全排列问题

全排列是另一个常见的深度优先搜索问题。给定一个不重复的数字序列,求出其所有可能的全排列。

💡 思路解析:

  • 使用一个递归函数dfs来遍历全排列。
  • 在递归函数中,通过循环遍历未被选中的数字,将当前数字加入到排列中,并将该数字标记为已选中。
  • 如果排列中的数字个数达到了序列的长度,将该排列输出。
  • 否则,递归调用dfs函数,深入到下一个数字的选择中。
  • 在回溯到上一个状态时,需要将当前数字从排列中移除,并将其标记为未选中,以便进行下一次选择。
#include <iostream>
#include <vector>
using namespace std;

void dfs(vector<int>& nums, vector<int>& path, vector<bool>& used, vector<vector<int>>& res) {
    if (path.size() == nums.size()) {
        res.push_back(path);
        return;
    }
    for (int i = 0; i < nums.size(); i++) {
        if (used[i]) {
            continue;
        }
        path.push_back(nums[i]);
        used[i] = true;
        dfs(nums, path, used, res);
        path.pop_back();
        used[i] = false;
    }
}

vector<vector<int>> permute(vector<int>& nums) {
    vector<vector<int>> res;
    vector<int> path;
    vector<bool> used(nums.size(), false);
    dfs(nums, path, used, res);
    return res;
}

int main() {
    vector<int> nums = {1, 2, 3};
    vector<vector<int>> permutations = permute(nums);
    for (const vector<int>& permutation : permutations) {
        for (int num : permutation) {
            cout << num << " ";
        }
        cout << endl;
    }
    return 0;
}

通过以上代码,我们可以得到数字序列的所有全排列。这个例子展示了

深度优先搜索在解决全排列问题时的应用。通过递归和回溯,我们可以遍历所有可能的排列情况。

🔢 素数环问题

素数环是指一组素数按照环状排列的情况,其中每两个相邻的素数的和也是素数。给定一个正整数n,求解n个数字的素数环。

💡 思路解析:

  • 定义一个长度为n的数组nums,用于存储素数环的数字。
  • 使用一个递归函数dfs来遍历素数环。
  • 在递归函数中,通过循环遍历1~n之间的数字,依次将每个数字赋值给当前位置。
  • 如果当前位置的数字与之前的数字构成素数,则继续递归深入。
  • 如果遍历到了最后一位数字,即构成了一个素数环,则输出该素数环。
  • 否则,递归调用dfs函数,深入到下一个位置的数字选择中。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

bool isPrime(int num) {
    if (num <= 1) {
        return false;
    }
    for (int i = 2; i <= sqrt(num); i++) {
        if (num % i == 0) {
            return false;
        }
    }
    return true;
}

void dfs(int depth, int n, vector<int>& nums, vector<bool>& visited) {
    if (depth == n) {
        if (isPrime(nums[0] + nums[n - 1])) {
            for (int num : nums) {
                cout << num << " ";
            }
            cout << endl;
        }
        return;
    }
    for (int i = 2; i <= n; i++) {
        if (!visited[i] && isPrime(nums[depth - 1] + i)) {
            nums[depth] = i;
            visited[i] = true;
            dfs(depth + 1, n, nums, visited);
            visited[i] = false;
        }
    }
}

void primeRing(int n) {
    vector<int> nums(n);
    vector<bool> visited(n + 1, false);
    nums[0] = 1;
    visited[1] = true;
    dfs(1, n, nums, visited);
}

int main() {
    int n = 4;
    primeRing(n);
    return 0;
}

通过以上代码,我们可以得到n个数字的素数环。在深度优先搜索过程中,我们通过判断相邻数字的和是否为素数来确定下一个数字的选择,从而构建出满足条件的素数环。

📚 总结

深度优先搜索是一种重要的算法思想,常用于解决图遍历、排列组合和搜索问题。通过递归和回溯的方式,我们可以遍历问题的所有可能解,并找到满足特定条件的解。通过以上的例子,我们掌握了深度优先搜索的基本原理和应用,希望能够在实际问题中灵活运用。

⭐️希望本篇文章对你有所帮助。

⭐️如果你有任何问题或疑惑,请随时向提问。

⭐️感谢阅读!

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

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

相关文章

【PCIE】协议分析之-hot-reset热复位

被上游指定热复位整个通路 当高层&#xff08;higher Layer&#xff09;指示某些通道进行热复位&#xff08;Hot Reset&#xff09;时&#xff0c;以下操作将被执行&#xff1a; 所有在配置的链路中的通道都会发送带有热复位位&#xff08;Hot Reset bit&#xff09;和配置的…

IntelliJ IDEA运行bat脚本,自动taskkill端口进程

在idea运行完程序后&#xff0c;再次重新运行时如果之前的程序仍然占用着端口会导致报错&#xff1a;端口被占用。 因此每次重新运行都需要移除之前的端口所在的进程&#xff0c;这对于调试开发过程是非常频繁的操作。 需要一个快速的办法直接移除端口进程&#xff0c;如下&a…

Haskell 入门学习(一)之安装试用 Haskell

Haskell 入门学习&#xff08;一&#xff09;之安装试用 Haskell 文章目录 Haskell 入门学习&#xff08;一&#xff09;之安装试用 Haskell前言&#xff1a;安装Windows 安装Linux、MacOs 使用 VSCode 进行代码编写创建一个简单的项目使用 Cabal 管理项目项目大致结构运行项目…

Verilog学习笔记3:与非门

1位的与非门 代码&#xff1a; timescale 1ns/10ps module nand_gate( A, B, Y);input A; input B; output Y;assign Y~(A&B);endmodule//testbenchmodule nand_gate_tb; reg A; reg B; wire Y;nand_gate nand_gate(.A(A),.B(B),.Y(Y));initial begin A<0;B<0;#10 …

Ubuntu创建Git项目并push到远程Github

首先在本地创建git仓库 jasminelhl:~/prj$ mkdir Github-test jasminelhl:~/prj$ cd Github-test jasminelhl:~/prj/Github-test$ git init 提示&#xff1a;使用 master 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中 提示&#xff1a;配置使用初始分支名&a…

MySQL之CONCAT、CONCAT_WS和GROUP_CONCAT函数用法

目录 一、准备数据 二、concat函数 1.语法格式 2.语法说明 3.场景使用 4.限制条件 三、concat_ws函数 1.语法格式 2.语法说明 3.场景使用 4.限制条件 四、group_conat函数 1.语法格式 2.语法说明 3.场景使用 4.限制条件 一、准备数据 #创建用户表 CREATE TABL…

[已解决]Springboot项目启动时端口被占用

目录 1.打开CMD窗口 2.找到端口号 3.找到对应程序 4.终止任务 5.再次查询端口&#xff0c;没有找到对应的端口号说明进行杀死成功 6. 重新启动项目&#xff0c;可以正常启动 Springboot端口号默认8080&#xff0c;启动时经常会遇到端口号被占用的情况&#xff0c;报错信息…

swift 打包xcframework报错“No ‘swiftinterface‘ files found within“

1、分别编译当前的framework&#xff0c;生成对应的真机framework与模拟器framework 生成对应framework.png 2、新建一个对应接收xcframework的文件夹&#xff0c;以XXX.xcframework格式命名 3、打开终端利用xcodebuild -create-xcframework命令进行生成xcframework。 具体如…

第一章 计算机网络概述【计算机网络】

第一章 计算机网络概述【计算机网络】 前言推荐第一章 计算机网络概述1.1 计算机网络在信息时代中的作用1.2互联网概述1.2.1 网络的网络1.2.2互联网基础结构发展的三个阶段1.2.3互联网的标准化工作 1.3互联网的组成1.3.1互联网的边缘部分1.3.2互联网的核心部分 1.4计算机网络在…

网络——网络排错

https://zhuanlan.zhihu.com/p/607083215 背景 出现网络故障时&#xff0c;我们需要做什么呢&#xff0c;做完网工&#xff08;不如dog&#xff09;&#xff0c;第一时间会被怀疑是网络问题&#xff0c;所以&#xff0c; 前端PC 前端PC能做的东西&#xff0c;不多&#xff0…

Go语言程序设计(十三)方法

Go语言虽然没有类(Class),但同样支持方法(Method),Go语言里的Method其实就是一个带接收者(Receiver)的函数。 一、Method的基本定义 Go语言中的Method类似于一个函数&#xff0c;只是函数名前多了一个绑定类型参数——receiver。 基本格式如下&#xff1a; func (recv recei…

启用 -parameters 编译选项简化 mybatis @Param 注解重复问题

在使用 mybatis 查询的时候, 只需要定义一个查询接口, mybatis 会为我们注入注解实现或是 xml 实现. 但当我们需要传递参数时, 通常需要 Param 来定义一个名称, 但经常的, 我们也不难发现, 这个名称与参数名称通常是一样的: User findUser(Param("username") String…

Docker使用Volumes做数据持久化

场景&#xff1a;docker 安装的 Pgsql 每当电脑重启docker重启 会发现 数据库都没了数据也没了 解决办法&#xff1a;使用docker volumes 做挂载 以PGSQL为例子&#xff0c;理论其他数据库或者项目需要持久化数据 应该都可以 1.创建Volumes 名字自己可以随便取。 2.拉去镜像…

开源预训练框架 MMPRETRAIN官方文档(概览、环境安装与验证、基础用户指南)

MMPretrain是全新升级的开源预训练框架。它已着手提供多个强大的预训练骨干网并支持不同的预训练策略。MMPretrain 源自著名的开源项目 MMClassification 和MMSelfSup&#xff0c;并开发了许多令人兴奋的新功能。目前&#xff0c;预训练阶段对于视觉识别至关重要。凭借丰富而强…

[SSM]MyBatis查询语句与动态SQL

目录 十、MyBatis查询语句专题 10.1返回Car 10.2返回List 10.3返回Map 10.4返回List 10.5返回Map,map> 10.6resultMap结果映射 使用resultMap进行结果映射 是否开启驼峰命名自动映射 10.7返回总记录条数 十一、动态SQL 11.1 if标签 11.2 where标签 11.3 trim标…

Oracle表空间和用户

Oracle表空间和用户 前言 1、创建表空间 2、 删除表空间 3、创建用户 4、用户赋权限 5、Oracle三个重要的角色 1、创建表空间 ORACLE 数据库的逻辑单元。 一个表空间可以与多个数据文件&#xff08;物理结构&#xff09;关联 一个数据库下可以建立多个表空间&#xff0c;一个表…

探秘制造业数智化:揭开低代码开发平台的新篇章

前言 随着数智时代的到来&#xff0c;制造业面临着前所未有的转型机遇。在这个信息爆炸的时代&#xff0c;如何有效运用科技和数据资源&#xff0c;成为了制造业企业的当务之急。而低代码开发平台&#xff0c;如JNPF快速开发平台&#xff0c;正是这个转型过程中不可或缺的利器。…

【编程语言 · C语言 · 结构体】

【编程语言 C语言 结构体】https://mp.weixin.qq.com/s/pWI712NxhPJi37eWrE9ofw

【产生初始解利器】基于蒙特卡洛模拟产生满足固定需求和固定供给的随机供给矩阵

如何生成一个总和是定值的随机矩阵 震惊&#xff0c;如果做一个约束比较强的模型&#xff0c;解的矩阵需要满足很多等式约束&#xff0c;而且都是整数&#xff0c;随机产生初始解很困难&#xff0c;该怎么办&#xff1f; 震惊&#xff0c;如果做一个约束比较强的模型&#xff0…

多元回归预测 | Matlab粒子群算法(PSO)优化极限梯度提升树XGBoost回归预测,PSO-XGBoost回归预测模型,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 粒子群算法(PSO)优化极限梯度提升树XGBoost回归预测,PSO-XGBoost回归预测模型,多变量输入模型,多变量输入模型,matlab代码回归预测,多变量输入模型,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高…