递归(四)—— 初识暴力递归之“打印字符串的全排列”

题目1:序列打印一个字符串的全排列

题目分析:结合一实例来理解题目,str = “abc”的全排列就是所求得的序列是 strp[0~2]的所有位的排列组合,strNew = {“abc”, “acb”, “bac”, “bca”,”cba”,”cab”}

思路1:枚举所有可能性, 假设str = “abcd”

Str[0]

Str[1]

Str[2]

Str[3]

a

b

c

d

d

c

c

b

d

d

b

d

c

b

b

c

b

a

c

d

d

c

c

a

d

d

a

d

a

c

c

a

c

a

b

d

d

b

b

a

d

d

a

d

a

b

b

a

c

a

b

d

d

b

b

a

d

d

a

d

a

b

b

a

从表格中可以看到,str[0] 有四种选择,{“a”,”b”, “c” , “d”};

当str[0]的选择确定后,str[1]的选择有3中,即在str[0]确定后,在剩下的三个字符中任意选择一个。

当str[0~1] 确定后,str[2]的选择有两个,即在str[0] 和str[1]选择后剩下的2个字符中,任意选择1个。

当str[0~2] 都确定后,只剩下1个字符,str[3] 只有唯一的字符可用。当没有任何字符可用于继续选择的时候,则认为得到了一个全排列组合。

代码实现

//代码段1
#include <list>
#include <string>
#include <iostream>//rest: 被选择后,剩余的字符;
//path;已经确定的字符所构成的字符串
//ans:存放每一次得到的字符串
void fun_1(vector<char> rest, string path, list<string>& ans) {if (rest.empty()) {ans.push_back(path);}else {int count = rest.size();for (int i = 0; i < count; i++) {char cur = rest[i];rest.erase(rest.begin() + i);fun_1(rest, path+cur, ans);rest.insert(rest.begin()+i, cur);}	}return;
}#define PERMUTATION //全排列int main() {
#ifdef PERMUTATIONlist<string> ans;string strInput = "abc";auto permutationFun= [](string str, list<string>& ans) {if (str.length() == 0) return;const char* arrChar = str.c_str();vector<char> rest(str.size());for (int i = 0; i < str.size(); i++) {rest[i] = arrChar[i];}string path = "";fun_1(rest, path, ans);};permutationFun(strInput, ans);pirnt_list(ans);
#endifreturn 0;
}

代码分析

当代码运行到第18行的时候,遇到了函数调用,再次进入fun_1,直到 rest.empty() == true, 表示没有剩余字符,所有字符都被使用,则将path存入ans。

当代执行到19行时,表示需要恢复现场。试想,我们每次为当前位选中一个字符后,则认为下一位不可以再使用这个字符,就需要将此字符中rest中删除,而当一个全排类完成后,需要将删除的字符重新加入到rest。

运行结果

当 strInput  = “abcd”;

思路2

任意一位与其他位进行交换,即可以得到一个新的全排列的组合,直到枚举所有的交换可能。

index_ 0 与其它位交换的可选性是三种,即 index_0 <-> index_0, index_0 <-> index_1, index_0 <-> index_2;

index_1 与其它位交换的选择性只有两种,即index_1 <->index_1 和index_1 <-> index_2;

Index_2 是最后一位,所以它与其它位交换的可能性只有一种 index_2 <-> index_2

当每一次的交换路径从头走到尾,即index_0 到index_n 都完成了交换,我们认为得到了一个全排列序列。接下来回到最近一次做不同决策的位置。例如上例,当执行:

 index_0 <->index_0 ==> index_1 <-> index_1 ==> index_2 <-> index_2 之后,最近一次决策就是index_2 <-> index_2, 因为它已经是最后一位,只有这一种决策,那么再回到这个决策的上一个决策,index_1 <-> index_1,这个决策是有不同分支的,index_1 <-> index_2 , 按照这条新路径继续走下去,直到最后一位。然后回到index_0 <-> index_0。

index_0 是有其它决策的,index_0 <-> index_1 ,然后按照这条路径继续交换下去,逻辑同上。

直达index_0 的所有决策分支都走完,则认为找到了所有的全排列序列。

代码实现

//代码段2
#include <list>
#include <string>
#include <iostream>void swap(string& str, int i, int j) {char temp = str[i];str[i] = str[j];str[j] = temp;
}void pirnt_list(list<string> listInput) {for (list<string>::iterator item = listInput.begin(); item != listInput.end(); item++) {std::cout << *item << std::endl;}
}//strPre: 每一次全排列后得到的字符串
//index: 当前需要确定的字符串位
//ans:存放每一次全排列得到的字符串
void fun_2(string strPre, int index, list<string>& ans) {if (index == strPre.length()) {ans.push_back(strPre);}else {for (int i = index; i < strPre.length(); i++) {swap(strPre, index, i);fun_2(strPre, index + 1, ans);swap(strPre, index, i);}}return;
}
#define PERMUTATION //全排列
int main() {  
#ifdef PERMUTATIONlist<string> ans;string strInput = "abcd";auto permutationFun = [](string str, list<string>& ans) {if (str.length() == 0) return;const char* arrChar = str.c_str();vector<char> rest(str.size());for (int i = 0; i < str.size(); i++) {rest[i] = arrChar[i];}string path = "";fun_2(str, 0, ans);};permutationFun(strInput, ans);pirnt_list(ans);
#endifreturn 0;
}

代码分析:

  1. 递归的base  case 就是 第22行 if(index == strPre.length()), 即当字符串中的所有index 都完成了交换,则将当前得到的序列存入ans
  2. 每一次将新的全排列序列写入ans后(即执行下一个支路前),都要恢复现场,即第29行代码.

运行结果

问题补充:打印一个字符串的全部排列,要求不出现重复排列。

问题分析:解决问题的思路与思路2是一样的, 只是在进行两个数据位交换的时候,增加一个判断,判断当前数据位与 要进行交换的数据位的字符是否相同,如果相同,则取消交换。

例如str = “aac”

index_0 与index_1 的字符相同,都是“a”,所以index_0 <-> index_1交换的这条支路的结果与 index _0  <->index_0 这条支路的结果式样的。所以,当判断到index_i <-> index_j 两个交换位的字符相同,则会推出交换。

代码实现

//代码段3
#include <list>
#include <string>
#include <iostream>void swap(string& str, int i, int j) {char temp = str[i];str[i] = str[j];str[j] = temp;
}void pirnt_list(list<string> listInput) {for (list<string>::iterator item = listInput.begin(); item != listInput.end(); item++) {std::cout << *item << std::endl;}
}void fun_3(string strPre, int index, list<string>& ans) {if (index == strPre.length()) {ans.push_back(strPre);}else {bool* visited = new bool[256];for (int i = 0; i < 256; i++) visited[i] = false;for (int i = index; i < strPre.length(); i++) {if (!visited[strPre[i]]) {visited[strPre[i]] = true;swap(strPre, index, i);fun_3(strPre, index + 1, ans);swap(strPre, index, i);}}delete[] visited;}return;
}#define PERMUTATION //全排列
int main() {  
#ifdef PERMUTATIONlist<string> ans;string strInput = "aac";auto permutationFun = [](string str, list<string>& ans) {if (str.length() == 0) return;const char* arrChar = str.c_str();vector<char> rest(str.size());for (int i = 0; i < str.size(); i++) {rest[i] = arrChar[i];}string path = "";fun_3(str, 0, ans);};permutationFun(strInput, ans);pirnt_list(ans);
#endifreturn 0;
}

代码分析

 设置标志位数据结构 bool* visited = new bool[256]; 初始化为false, visited[i] 表示对应assicll码为i

的字符是否出现过。

for (int i = index; i < strPre.length(); i++) {
            if (!visited[strPre[i]]) {
               ....
            }

 }

i 位置上的字符与index上字符要交换,只有i 位置上的字符是不曾出现过的,才会交换,如果visiited[strpre[i]] == ture, 表示在这条路基上这个字符与index发生过交换,只要交换过这个字符,就被置为true。

运行结果:

str = "aac",  使用fun_2 没有去重处理:

使用fun_3 去重处理函数: 

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

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

相关文章

旷野之间3 – CTO 应具备的技能

​​​​​​ 随着技术渗透到商业的各个方面,首席技术官的角色变得越来越具有战略性和多面性。虽然深厚的技术技能仍然是基础,但今天的首席技术官还需要具备领导能力、商业敏锐度、沟通能力等优势。 根据我作为 CTO 的个人经验,我将深入探讨现代 CTO 所需的各种能力,包括:…

vue中,图片在div中按照图片原来大小等比例显示

图片在div中按照图片原来大小等比例显示&#xff0c;可以保证web上显示的图片和实际图片形状一样&#xff0c;保留原始图片效果 实现代码如下&#xff1a; <div style"padding: 0; width:400px;height:400px;position: absolute;border: 1px solid #eff2f6;">…

百问网全志D1h开发板红外控制LVGL界面切换

红外控制LVGL界面切换 1. 测试红外功能 1.1 配置设备树 查看原理图&#xff1a; 可以看到红外对应的引脚号是PG16。 进入目录&#xff1a; cd /home/ubuntu/tina-d1-h/device/config/chips/d1-h/configs/nezha/linux-5.4修改board.dts&#xff1a; vim board.dts修改引…

MUNIK解读ISO26262:安全计划

前言 当我们进行功能安全开发时&#xff0c;由于整个项目周期和内容较多&#xff0c;因此需要在项目前期对一些问题提前进行规划&#xff1a;比如功能安全开发具体分为几个阶段&#xff0c;应该怎么去做&#xff1f;对于不同的环节&#xff0c;有哪些人员来执行&#xff1f;资…

在网上申请流量卡审核失败,可能是你的年龄有问题!

在网上申请流量卡审核失败&#xff0c;可能是你的年龄有问题&#xff01; 先上个图&#xff1a; ​ 网上的流量卡并不是随意申请的&#xff0c;而是填写申请信息后由运营商进行审核&#xff0c;审核通过后才会发卡&#xff0c;如果你提交的订单没有审核通过&#xff0c;那么大…

体积大的快递怎么寄便宜?如何寄件寄包裹更省钱?

大学毕业了&#xff0c;面对即将到来的工作生活&#xff0c;小李不得不把宿舍里的大包小包打包寄回家。可是&#xff0c;当他真正开始打包行李时&#xff0c;才发现这可不是一件简单的事&#xff1a;衣服、被子、书籍、杂物……这些东西加起来体积不小&#xff0c;想要省钱寄快…

HippoRAG如何从大脑获取线索以改进LLM检索

知识存储和检索正在成为大型语言模型(LLM)应用的重要组成部分。虽然检索增强生成(RAG)在该领域取得了巨大进步&#xff0c;但一些局限性仍然没有克服。 俄亥俄州立大学和斯坦福大学的研究团队推出了HippoRAG&#xff0c;这是一种创新性的检索框架&#xff0c;其设计理念源于人类…

强化学习实战1:OpenAI Gym 实验环境介绍

环境配置 我的 torch 版本是 2.3.0&#xff0c;然后 gym 版本是 0.22.0&#xff0c;python 版本是 3.8 &#xff0c;pygame 版本是 2.6.0 。 首先安装一下 gym&#xff1a; pip install gym0.22.0 -i https://pypi.tuna.tsinghua.edu.cn/simple然后安装一下 pygame&#xff…

Linux服务器CPU占用率达到100%排查思路

1、找到最耗CPU的进程pid&#xff0c;执行命令 top 2、找到最耗CPU的线程tid // 执行 top -Hp [pid] 定位应用进程对应的线程 tid // 按shift p 组合键&#xff0c;按照CPU占用率排序 > top -Hp 14246 3、将线程pid转化为16进制 // printf "%x\n" [tid] 将tid…

Sharding-JDBC分库分表之SpringBoot主从配置

Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 前言 在开发中&#xff0c;如果对数据库的读和写都在一个数据服务器中操作&#xff0c;面对日益增加的访问量&#x…

无法找到模块“@wangeditor/editor-for-vue”的声明文件

vue3项目中使用wangeditor/editor遇到的问题 开发环境不管红线报错正常使用 打包的时候就会报错了 1.安装依赖 pnpm install --save wangeditor/editor wangeditor/editor-for-vuenext 2.遇到的问题 3.解决方法 在src目录下面创建 wangeditor-types.d.ts 文件 代码如下 de…

【ai_agent】从零写一个agent框架(五)基于egui制作一个agent/workflow在线编辑器

前言 上篇我们实现了基础节点&#xff0c;并暴露出grpc服务&#xff0c;但是手动编辑文本制作一个workflow实在强人所难。 所以本文我们做个webui自动生成workflow。 开搞之前先看看别人怎么做的。 Dify 的ui 效果如下图示&#xff1a; 支持多种功能节点 但只能打开一个节…

【线性表,线性表中的顺序表和链表】

目录 1、线性表的定义和基本操作1.1、线性表的定义1.2、线性表的基本操作 2、顺序表和链表的比较2.1、顺序表2.1.1、顺序表的定义和特点2.1.2、顺序表的实现&#xff08;1&#xff09;顺序表的静态分配&#xff1a;&#xff08;2&#xff09;顺序表的动态分配 2.1.3、顺序表的基…

基于正点原子的FreeRTOS笔记——队列

一、什么是队列 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制。 在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”&#xff0c;队列能够存储“队列项目”的最大数量称为队列的长度。 在创建队列时要指定队列长度以及队列项目…

蹭一个围棋亚军!不要和低维的人说话——早读(逆天打工人爬取热门微信文章解读)

熬夜后需要补什么呢&#xff1f; 引言Python 代码第一篇 洞见 不要和低维的人说话&#xff08;深度好文&#xff09;第二篇 冲冲冲结尾 引言 昨晚真的是熬夜又想不出东西 真的头大 最近下围棋 这个棋感很好呀 我是K级选手 目前是8级 套几个buff 纯自学 为什么决定学围棋呢? 是…

翰德恩咨询赋能材料行业上市公司,共筑IPD管理体系新篇章

赋能背景概览 坐落于江苏的某材料行业领军企业&#xff0c;作为国内无机陶瓷膜元件及成套设备领域的佼佼者&#xff0c;以其庞大的生产规模、丰富的产品系列及卓越的研发实力&#xff0c;屹立行业之巅二十余年。公司不仅在新材料研发、技术创新、工艺设计、设备制造及整体解决…

swift与Internvl下的多模态大模型分布式微调指南(附代码和数据)

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 基于Dify的智能分类方案&#xff1a;大模型结合KNN算法&#xff08;附代码&#xff…

InavFlight飞控固件学习-1《开发环境搭建》

目录 文章目录 目录摘要1.官网2.形成Linux开发环境工具2.1 简介2.2 相关工具2.2.1 Ubuntu / Debian系统配置命令2.2.2 Fedora系统配置命令2.2.3 Fedora系统配置命令 2.3 克隆存储库2.4 构建工具2.5 使用cmake2.6 构建固件2.7 清除2.8 cmake 缓存维护2.9 编译通过ninja2.10 更新…

小程序跳企业微信教程

来别急&#xff0c;我说几个点&#xff0c;你先记着&#xff0c; 必须真机才能测必须要企业微信里头去配置下关联到对应的小程序&#xff08;文章底部有图&#xff09;要同一个公司主体&#xff01; 说说几个报错&#xff0c; 一、代码其实就这么一个&#xff0c;你别在微信…