算法妙妙屋-------1.递归的深邃回响:全排列的奇妙组合

全排列的简要总结

全排列(Permutation)是数学中一个经典的问题,指的是从一组元素中,将所有元素按任意顺序排列形成的所有可能序列。


特点

  1. 输入条件

    • 给定一组互异的元素(通常为数组或字符串)。
    • 例如:[1, 2, 3] 的全排列。
  2. 输出结果

    • 输出所有元素的排列组合。
    • 例如:[1, 2, 3] 的全排列是:
      [1, 2, 3]
      [1, 3, 2]
      [2, 1, 3]
      [2, 3, 1]
      [3, 1, 2]
      [3, 2, 1]
      
  3. 数量公式

    • 对于 n 个互异元素,其全排列数量为 n ! n! n!(阶乘)。
    • 例如:n = 3 时,全排列数量为 3 ! = 6 3! = 6 3!=6

实现方式

1. 递归法

通过递归交换或回溯实现:

#include <iostream>
#include <vector>
using namespace std;void backtrack(vector<int>& nums, int start, vector<vector<int>>& result) {if (start == nums.size()) {result.push_back(nums);return;}for (int i = start; i < nums.size(); i++) {swap(nums[start], nums[i]);           // 交换当前元素backtrack(nums, start + 1, result);  // 递归处理子问题swap(nums[start], nums[i]);           // 撤销交换(回溯)}
}int main() {vector<int> nums = {1, 2, 3};vector<vector<int>> result;backtrack(nums, 0, result);for (const auto& perm : result) {for (int num : perm) {cout << num << " ";}cout << endl;}return 0;
}
2. STL 函数

C++ 提供了 std::next_permutation,可以简单地生成字典序全排列:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {vector<int> nums = {1, 2, 3};do {for (int num : nums) {cout << num << " ";}cout << endl;} while (next_permutation(nums.begin(), nums.end()));  // 调用 STL 函数return 0;
}

应用场景

  1. 组合数学

    • 求解排列问题、旅行商问题等。
  2. 字符串操作

    • 对字符串生成不同排列,用于密码学、模式匹配等。
  3. 游戏与搜索

    • 如数独解法、八皇后问题等,依赖全排列进行搜索。
  4. 算法优化

    • 通过排列测试不同的输入顺序,寻找最优解。

优化与注意事项

  1. 剪枝优化

    • 在递归中排除不合法或重复的排列,提高效率。
  2. 重复元素

    • 如果输入包含重复元素,需要特殊处理以避免重复结果。
  3. 大规模问题

    • 对于规模较大的输入,因 n ! n! n! 增长较快,可能需要改用启发式算法。

全排列是基础的数学与算法问题,其思想在递归、动态规划和搜索算法中广泛应用!​

1.全排列

题目链接:全排列
题目概述:
在这里插入图片描述
解题思路:递归流程如下:

  1. ⾸先定义⼀个⼆维数组res⽤来存放所有可能的排列,⼀个⼀维数组ans⽤来存放每个状态的排
    列,⼀个⼀维数组visited标记元素,然后从第⼀个位置开始进⾏递归;
  2. 在每个递归的状态中,我们维护⼀个步数step,表⽰当前已经处理了⼏个数字;
  3. 递归结束条件:当step等于nums数组的⻓度时,说明我们已经处理完了所有数字,将当前数组
    存⼊结果中;
  4. 在每个递归状态中,枚举所有下标i,若这个下标未被标记,则使⽤nums数组中当前下标的元
    素:
    a. 将visited[i]标记为1;
    b. ans数组中第step个元素被nums[i]覆盖;
    c. 对第step+1个位置进⾏递归;
    d. 将visited[i]重新赋值为0,表⽰回溯;
  5. 最后,返回res。
    • 特别地,我们可以不使⽤标记数组,直接遍历step之后的元素(未被使⽤),然后将其与需要递
    归的位置进⾏交换即可。
class Solution {vector<vector<int>> it;vector<int> path;bool check[7];//check用来存储是否被使用过public:void dfs(vector<int>& nums, vector<int>& path) {if (path.size() == nums.size()) {it.push_back(path);}else {for (int a = 0; a < nums.size(); a++) {if (!check[a]) {path.push_back(nums[a]);check[a] = true;//标记,下次不能再选dfs(nums, path);check[a] = false;//回溯复原path.pop_back();}}}}vector<vector<int>> permute(vector<int>& nums) {dfs(nums, path);return it;}
};

2.子集

题目链接:子集
题目介绍:

在这里插入图片描述
解题思路:

  1. 递归结束条件:如果当前需要处理的元素下标越界,则记录当前状态并直接返回;
  2. 在递归过程中,对于每个元素,我们有两种选择:
    ◦ 不选择当前元素,直接递归到下⼀个元素;
    ◦ 选择当前元素,将其添加到数组末尾后递归到下⼀个元素,然后在递归结束时撤回添加操作;
  3. 所有符合条件的状态都被记录下来,返回即可。
class Solution {
public:vector<vector<int>> ret;vector<int> path;void dfs(vector<int>& nums, int pos) {if (pos == nums.size()) {ret.push_back(path);return;}// 选path.push_back(nums[pos]);dfs(nums, pos + 1);path.pop_back(); // 恢复现场// 不选dfs(nums, pos + 1);}vector<vector<int>> subsets(vector<int>& nums) {dfs(nums, 0);return ret;}
};

3.全排列||(!!!)

题目链接:全排列
题目介绍:
在这里插入图片描述
解题思路:
因此,我们需
要对相同元素定义⼀种规则,使得其组成的排列不会形成重复的情况:

  1. 我们可以将相同的元素按照排序后的下标顺序出现在排列中,通俗来讲,若元素s出现x次,则排序后的第2个元素s⼀定出现在第1个元素s后⾯,排序后的第3个元素s⼀定出现在第2个元素s后⾯,以此类推,此时的全排列⼀定不会出现重复结果。
  2. 例如:a1=1,a2=1,a3=2,排列结果为[1,1,2]的情况只有⼀次,即a1在a2前⾯,因为a2不会出现在a1前⾯从⽽避免了重复排列。
  3. 我们在每⼀个位置上考虑所有的可能情况并且不出现重复;
  4. 注意:若当前元素的前⼀个相同元素未出现在当前状态中,则当前元素也不能直接放⼊当前状态的数组,此做法可以保证相同元素的排列顺序与排序后的相同元素的顺序相同,即避免了重复排列出现。
  5. 通过深度优先搜索的⽅式,不断地枚举每个数在当前位置的可能性,并在递归结束时回溯到上⼀个状态,直到枚举完所有可能性,得到正确的结果。
class Solution {vector<int> path;vector<vector<int>> ret;bool check[9];public:vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(), nums.end());//先进行排序dfs(nums);return ret;}void dfs(vector<int>& nums) {if (path.size() == nums.size()) {ret.push_back(path);return;}for (int i = 0; i < nums.size(); i++) {// 剪枝if (check[i] == false &&(i == 0 || nums[i] != nums[i - 1] || check[i - 1] != false))//非常的巧妙,想要插入必须满足以下要求   1.没有被插入过   2.第一位(没有前一项)||当前项和其前一项不一样||前一项已经被插入过了{path.push_back(nums[i]);check[i] = true;dfs(nums);path.pop_back(); // 恢复现场check[i] = false;}}}
};

4.括号生成(!!!)

题目链接:括号生成
题目介绍:在这里插入图片描述

解题思路:
从左往右进⾏递归,在每个位置判断放置左右括号的可能性,若此时放置左括号合理,则放置左括号
继续进⾏递归,右括号同理。
⼀种判断括号是否合法的⽅法:从左往右遍历,左括号的数量始终⼤于等于右括号的数量,并且左括
号的总数量与右括号的总数量相等。因此我们在递归时需要进⾏以下判断:

  1. 放⼊左括号时需判断此时左括号数量是否⼩于字符串总⻓度的⼀半(若左括号的数量⼤于等于字符
    串⻓度的⼀半时继续放置左括号,则左括号的总数量⼀定⼤于右括号的总数量);
  2. 放⼊右括号时需判断此时右括号数量是否⼩于左括号数量。
class Solution {
public:vector<string> str;void dfs(string it, int left,int right){if(left==0)//做括号全部用完{while(right){it.push_back(')');right--;}str.push_back(it);return;}it.push_back('(');//插入左括号dfs(it,left-1,right);it.pop_back();//回溯,回复现场if(right>left)//右括号剩余必须比左括号多{it.push_back(')');dfs(it,left,right-1);it.pop_back();//回溯}}vector<string> generateParenthesis(int n) {string it;int left=n;int right=n;it.push_back('(');//左边先插入一个(,避免第一个就是)的错误left--;dfs(it,left,right);return str;}
};

5.组合

题目链接:组合
题目介绍:
在这里插入图片描述
题⽬要求我们从1到n中选择k个数的所有组合,其中不考虑顺序。也就是说,[1,2]和[2,1]等价。我
们需要找出所有的组合,但不能重复计算相同元素的不同顺序的组合。对于选择组合,我们需要进⾏
如下流程:

  1. 所有元素分别作为⾸位元素进⾏处理;
  2. 在之后的位置上同理,选择所有元素分别作为当前位置元素进⾏处理;
  3. 为避免计算重复组合,规定选择之后位置的元素时必须⽐前⼀个元素⼤,这样就不会有重复的组合
    ([1,2]和[2,1]中[2,1]不会出现)。
class Solution {
public:bool check[20];vector<vector<int>> it;vector<int> flag;void dfs(int n, int k, int first) {if (k) {for (int i = first; i <= n; i++) {flag.push_back(i);dfs(n, k-1,i+1);flag.pop_back();}}else{it.push_back(flag);}}vector<vector<int>> combine(int n, int k) {dfs(n, k, 1);return it;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Rust】unsafe rust入门

这篇文章简单介绍下unsafe rust的几个要点 1. 解引用裸指针 裸指针其实就是C或者说C的指针&#xff0c;与C的指针不同的是&#xff0c;Rust的裸指针还是要分为可变和不可变&#xff0c;*const T 和 *mut T&#xff1a; 基于引用创建裸指针 let mut num 5;let r1 &num …

什么是人工智能大模型?

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于人工智能大模型的相关内容&#xff01; …

基于深度学习和卷积神经网络的乳腺癌影像自动化诊断系统(PyQt5界面+数据集+训练代码)

乳腺癌是全球女性中最常见的恶性肿瘤之一&#xff0c;早期准确诊断对于提高生存率具有至关重要的意义。传统的乳腺癌诊断方法依赖于放射科医生的经验&#xff0c;然而&#xff0c;由于影像分析的复杂性和人类判断的局限性&#xff0c;准确率和一致性仍存在挑战。近年来&#xf…

【IMF靶场渗透】

文章目录 一、基础信息 二、信息收集 三、flag1 四、flag2 五、flag3 六、flag4 七、flag5 八、flag6 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机IP&#xff1a;192.168.20.147 二、信息收集 Nmap -sP 192.168.20.0/24 Arp-scan -l nmap -sS -sV -p- -…

MySQL 复合查询

实际开发中往往数据来自不同的表&#xff0c;所以需要多表查询。本节我们用一个简单的公司管理系统&#xff0c;有三张表EMP,DEPT,SALGRADE 来演示如何进行多表查询。表结构的代码以及插入的数据如下&#xff1a; DROP database IF EXISTS scott; CREATE database IF NOT EXIST…

理解Java集合的基本用法—Collection:List、Set 和 Queue,Map

本博文部分参考 博客 &#xff0c;强烈推荐这篇博客&#xff0c;写得超级全面&#xff01;&#xff01;&#xff01; 图片来源 Java 集合框架 主要包括两种类型的容器&#xff0c;一种是集合&#xff08;Collection&#xff09;&#xff0c;存储一个元素集合&#xff08;单列…

【看海的算法日记✨优选篇✨】第三回:二分之妙,寻径中道

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 一念既出&#xff0c;万山无阻 目录 &#x1f4d6;一、算法思想 细节问题 &#x1f4da;左右临界 &#x1f4da;中点选择 &#x1f4da;…

[CTF/网络安全] 攻防世界 upload1 解题详析

[CTF/网络安全] 攻防世界 upload1 解题详析 考察文件上传&#xff0c;具体原理及姿势不再赘述。 姿势 在txt中写入一句话木马<?php eval($_POST[qiu]);?> 回显如下&#xff1a; 查看源代码&#xff1a; Array.prototype.contains function (obj) { var i this.…

网络安全运行与维护 加固练习题

1. 提交用户密码的最小长度要求。 输入代码: cat /etc/pam.d/common-password 提交答案: flag{20} 2.提交iptables配置以允许10.0.0.0/24网段访问22端口的命令。 输入代码: iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 22 -j ACCEPT 提交答案: flag{iptables -A I…

PID模糊控制算法(附MATLAB仿真程序)

一、基本原理 PID模糊控制算法是一种将传统PID控制与模糊逻辑相结合的控制策略。它利用模糊逻辑处理不确定性和非线性问题的能力&#xff0c;以提高控制系统的性能。以下是PID模糊控制算法的基本原理&#xff1a; 1.1. **误差和误差变化率的计算**&#xff1a; - 首先&…

【leetcode100】螺旋矩阵

1、题目描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 2、初始思路 2.1 思路 定义上下左右…

2024.11.29(单链表)

思维导图 声明文件 #ifndef __LINKLIST_H__ #define __LINKLIST_H__#include <myhead.h>typedef char datatype; //数据元素类型 //定义节点类型 typedef struct Node {union{int len; //头节点数据域datatype data; //普通节点数据域};struct Node *next; //指针域…

第六届金盾信安杯-SSRF

操作内容&#xff1a; 进入环境 可以查询网站信息 查询环境url https://114.55.67.167:52263/flag.php 返回 flag 就在这 https://114.55.67.167:52263/flag.php 把这个转换成短连接&#xff0c;然后再提交 得出 flag

【Linux】进程控制,手搓简洁版shell

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 1、进程创建2、进程终止3、进程等待4、进程程序替换5、手写简洁版shell 1、进程创建 fork函数&#xff1a;从已经存在的进程中创…

逆向攻防世界CTF系列42-reverse_re3

逆向攻防世界CTF系列42-reverse_re3 参考&#xff1a;CTF-reverse-reverse_re3&#xff08;全网最详细wp&#xff0c;超4000字有效解析&#xff09;_ctfreverse题目-CSDN博客 64位无壳 _int64 __fastcall main(__int64 a1, char **a2, char **a3) {int v4; // [rsp4h] [rbp-…

安装 RabbitMQ 服务

安装 RabbitMQ 服务 一. RabbitMQ 需要依赖 Erlang/OTP 环境 (1) 先去 RabbitMQ 官网&#xff0c;查看 RabbitMQ 需要的 Erlang 支持&#xff1a;https://www.rabbitmq.com/ 进入官网&#xff0c;在 Docs -> Install and Upgrade -> Erlang Version Requirements (2) …

ECharts柱状图-交错正负轴标签,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

Scala关于成绩的常规操作

score.txt中的数据&#xff1a; 姓名&#xff0c;语文&#xff0c;数学&#xff0c;英语 张伟&#xff0c;87&#xff0c;92&#xff0c;88 李娜&#xff0c;90&#xff0c;85&#xff0c;95 王强&#xff0c;78&#xff0c;90&#xff0c;82 赵敏&#xff0c;92&#xff0c;8…

【机器学习】入门机器学习:从理论到代码实践

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 机器学习&#xff08;Machine Learning&#xff09;是人工智能的一个分支&#xff0c;它通过算法从数据中学习规律&#xff0c;并基于这些规律进行…

Spring Web开发(请求)获取JOSN对象| 获取数据(Header)

大家好&#xff0c;我叫小帅今天我们来继续Spring Boot的内容。 文章目录 1. 获取JSON对象2. 获取URL中参数PathVariable3.上传⽂件RequestPart3. 获取Cookie/Session3.1 获取和设置Cookie3.1.1传统获取Cookie3.1.2简洁获取Cookie 3. 2 获取和存储Session3.2.1获取Session&…