C++字符串全排列(递归法)和(迭代法)以及next_permutation底层原理详解

2023-11-10

前言

next_permutation/prev_permutation是C++ STL中的一种实用算法;

功能是: 以迭代器的方式,将一个容器内容改变为他的下一个(或prev上一个)全排列组合;

在这里插入图片描述

next_permutation的使用

假设需要将字符串abcd的全排列依次打印,我们可以用next_permutation函数方便操作:

使用方法:

  1. 一般先sort成升序;(prev_permutation倒着全排列使用规则相反)

  2. 然后再do while配合调用全排列,循环输出,直到排列情况全部输出 返回false;

在这里插入图片描述

运行结果:

在这里插入图片描述

实现全排列的两种算法

当然仅仅只会用没有什么困哪的,如果面试官突然问你STL中这个全排列算法咋实现的呢?

1. 递归法(全排列方便理解记忆的方法,作为备用方法)

如果上面全排列,突然脑袋断片了,或者说考试中不让用封装好的库函数;

为了不至于连个全排列的思想都不会,可以用用相对好理解的递归法全排列

算法思想:(递归问题:按规则处理一个过程,剩下的过程是相同的处理方式,那么就可以进行函数递归调用)

  1. 从集合中依次选出每一个元素,作为排列的第一个元素
  2. 然后**对剩余的元素(第一个元素之后的)进行 同样操作 **;

如此递归处理,从而得到所有元素的全排列。

eg:

  • 以对字符串abc进行全排列为例,我们可以这么做:以abc为例

固定a,递归求后面bc的排列:求好: abc,acb后,b交换到第一位置,得到bac,如下固定b递归b后面的排列:
固定b,求后面ac的排列:bac,bca,求好后,c交换到第一位置,得到cba,如下固定c递归c后面的排列:
固定c,求后面ba的排列:cba,cab;结束 一共a,b,c分别当第一个元素进行了全排列,算法结束;

(注意:1. 每次交换下一个位置的时候,需要swap换回来,保证原始序列,再交换下一个位置的字母去第一个位置。2. 需要考虑有重复相同且挨着的数字情况,此时需要剪枝)

递归比较抽象,可以用简单地例子abc在纸上模拟画一下好理解;

实现代码(无重复元素情况)

#include<iostream>
#include<vector>
using namespace std;

//dfs实现全排列(无重复元素情况)
void dfs(string &s, int l, int r)
{
	if (l == r) {//递归终止,当前s可以输出了,已经是某一轮的完整排列,不能再排列了
		cout<<s<<endl;
		return;
	}

	for (int i = l; i < r; i++) {
        
            swap(s[l],s[i]);
			dfs(s,l+1,r);//递归
			swap(s[l], s[i]);//进行下一轮的swap dfs,需要先swap换回来原来的位置!否则会出现重复排列!
		
	}
}


int main()
{
	string s = "abcd";
    //sort(s.begin(),s.end())//sort一下,再配合dfs算法,可以实现按照字典序处理
    int len = s.size();
	dfs(s,0,len);
	return 0;
}

运行结果:

在这里插入图片描述

有重复元素情况

在这里插入图片描述

这种情况需要对代码做一个优化,不然的话按照上面的算法,会出现重复数字的重复排列情况;

优化很简单:

如果某个数字在之前的排列被换到首位置进行排列过,那本次交换就不进行;(其次,当dfs首次交换i==l的时候,即便出现过也需要进行);

整合一下就是if(i==l || s[i]没出现) -->进行交换)

代码:

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;

//dfs实现全排列(含重复元素情况)
void dfs(string &s, int l, int r)
{
	if (l == r) {
		cout<<s<<endl;
		return;
	}
	set<char>st;//检测重复的set
	for (int i = l; i < r; i++) {
		if (i == l || st.find(s[i])==st.end()) {//防止后续进行重复排列
			st.insert(s[i]);//满足  记录这个字符 

			swap(s[l], s[i]);
			dfs(s, l + 1, r);
			swap(s[l], s[i]);
		}
	}
}


int main()
{
	string s = "aba";
	int len = s.size();
	dfs(s,0,len);
	return 0;
}

2. 迭代法(next_permutation底层原理)

比较抽象,难以理解,根据个人情况来理解;

一个全排列可看做一个字符串,字符串可有前缀、后缀。
规定: 生成给定全排列的下一个排列–> 所谓一个字符串的下一个排列,就是这个个字符串变化限制在尽可能短的后缀上,变化后的那个字符串; 这就要求这一个与下一个有尽可能长的共同前缀;变化限制在尽可能短的后缀上
eg:

839647521是1—9的排列。1—9的排列最前面的是123456789,最后面的987654321;

从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。


如何得到346987521的下一个?

  • 首先对原生字符串排序,这个迭代算法是基于字典序排序好的字符串全排列;(所以之后从尾部,向前循环迭代,每次变化尽可能短的后缀,以此类推)
  1. 从尾部往前找第一个P(i-1) < P(i)的位置;: 346987521 种 最终找到6是第一个变小的数字,记录下6的位置i-1
  2. 从找到的 i 位置往后找到最后一个大于6的数:346987521 中最终找到7的位置,记录位置为m(m == r-1)
  3. swap(r-1,i-1) : 3 4 7 9 8 6 5 2 1
  4. 倒序翻转i位置后的所有数据 : 3 4 7 1 2 5 6 8 9
  5. 进行do-while循环,直到第一步之后,判断出 i==0 break; 全部排列完毕

很抽象,但是思想就是 一个字符串的下一个全排列:有尽可能长的共同前缀;变化限制在尽可能短的后缀上

大概知道步骤: 那么用排好序的123456789试试上面的过程,你会发现,每次变化尽可能短的后缀,有点像递归的感觉,一步一步逼近字符串0,index,此时完美结束; 或者用123这个例子体验一下6个全排列是咋来的 amazing

上面的过程多悟几遍就理解了; 大概知道思想面试的时候说说也行=-=

实现代码(有无重复不影响)

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;

//dfs实现全排列



int main()
{
	string s = "abcd";
	
	sort(s.begin(),s.end());//记得先排序
	
	int len = s.size();
	do
	{
		cout << s << endl;//打印某次排列

		int i = s.size() - 1;
		int j;
		while (i > 0 && s[i] <= s[i - 1]) i--;//1.从后向前 找 第一个 s[i-1]<s[i]

		if (i == 0) break;

		j = i;

		while (j<len && s[j]>s[i - 1]) j++;//2.从i向后 找 最后一个 s[m]>s[i-1] 用j找,所以最后m==j-1

		swap(s[i - 1], s[j - 1]);//3. swap (i-1,m(j-1))

		reverse(s.begin() + i, s.end());  //4. 翻转 i后面的子串

		

	} while (1); //do while为了让第一个 abcd 也正常打印再全排列

	return 0;
}

这个算法理解起来对于我来说有点夸张,呜呜呜;

牛客题目链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++字符串全排列(递归法)和(迭代法)以及next_permutation底层原理详解 的相关文章

随机推荐

  • anaconda 安装 for macOSX

    步骤 1 登陆官网 https www anaconda com 2 点击 get started 开始 3 选择下载 下载 4 根据自己的电脑来选择下载的方式 我用的是mac所以就选择这个 这里有两种选择 其实都可以 选择一个就行 4 1
  • osgEarth的Rex引擎原理分析(二十一)创建瓦片模型过程详解

    目标 十七 中问题47 瓦片模型的作用是负责管理瓦片中的影像 高程等图层信息 这些信息的获取最终通过createTileModel函数来实现 负责维护瓦片版本等 应当是每一个瓦片都对应有一个瓦片模型 这个瓦片模型是在瓦片请求过程中创建的 具
  • 面试最常被问的 Java 后端题目及参考答案

    一 Java 基础篇 1 Object 有哪些常用方法 大致说一下每个方法的含义 2 Java 创建对象有几种方式 3 获取一个类对象的方式有哪些 4 ArrayList 和 LinkedList 的区别有哪些 5 用过 ArrayList
  • next.js中引入sass

    第一步 安装sass npm install save zeit next sass node sass 第二步 在项目根目录添加 next config js 文件 用于指示Next加载对用的功能 const withSass requi
  • 基础软件与开发语言开源论坛

    ChinaOSC 2022基础软件与开发语言开源技术论坛将于8月20日 14 00 18 00在陕西省西安高新国际会议中心召开 论坛邀请到在操作系统 中间件等基础软件领域 以及编程语言领域深耕多年的开源专家 深度分享开源软件研发的创新之路
  • 【Pandas总结】第九节 Pandas_累计与分组 pd.groupby()

    文章目录 一 数据准备 二 累计值计算 2 1 df describe 2 2 常用统计值 三 分组 pd groupby 四 更多的使用方法 aggregate filter transform apply 4 1 aggregate 4
  • [Kafka] - Kafka Java Producer代码实现

    根据业务需要可以使用Kafka提供的Java Producer API进行产生数据 并将产生的数据发送到Kafka对应Topic的对应分区中 入口类为 Producer Kafka的Producer API主要提供下列三个方法 public
  • webview拦截垃圾电信运营商的广告方法

    mWebView setWebViewClient new WebViewClient Override public boolean shouldOverrideUrlLoading WebView view String url if
  • terminated 线程_Java中如何正确地中断一个线程?

    本文主要整理了关于线程中断的相关知识点 1 线程的状态 NEW 新建 一个尚未启动的线程处于这一状态 A thread that has not yet started is in this state RUNNABLE 可运行 一个正在
  • QT实现塔防游戏

    在基本功能实现后 对游戏进行优化 主要有以下几部分 实现传奇的升级 实现传奇的移除 绘画出波数与血量 实现游戏的暂停与回到选关关卡 实现游戏的胜利与失败 1 实现传奇的升级 创建一个selectbutton2类 ifndef SELECTB
  • 【华为OD机试真题 Python】多个数组合并

    前言 本专栏将持续更新华为OD机试题目 并进行详细的分析与解答 包含完整的代码实现 希望可以帮助到正在努力的你 关于OD机试流程 面经 面试指导等 如有任何疑问 欢迎联系我 wechat steven moda email nansun09
  • 如何让RecyclerView滑动到底部?

    在做这个功能时 使用scroll的任何一个方法 发现它每次都只滑到了一半 今天终于解决了 解决方法如下 LinearLayoutManager linearLayoutManager LinearLayoutManager recycler
  • 【批量注册组件】

    自动的批量注册组件 大致步骤 使用 require 提供的函数 context 加载某一个目录下的所有 vue 后缀的文件 然后 context 函数会返回一个导入函数 importFn 它又一个属性 keys 获取所有的文件路径 通过文件
  • 不得不读

    本次总共收集了8位大牛的8篇精品文章 内容涉及设计 验证 行业研究 ICer职场生活等各方面 欢迎大家点击阅读并关注 1 酒酒拿下四五十万的真实大厂面试经历 作者介绍 酒酒成电研三在读 自学算法leetcode刷题 双修IC验证 斩获互联网
  • 程序中难以捉摸的错误如何自动检测?Parasoft Insure++ v2021.1发布!

    Parasoft Insure 是用于 C 和 C 应用程序的自动化运行时应用程序测试工具 可检测难以捉摸的错误 例如内存损坏 内存泄漏 内存分配错误 变量初始化错误 变量定义冲突 指针错误 库错误 I O 错误 和逻辑错误 Parasof
  • Python网络爬虫使用教程

    文章目录 一 URL资源抓取 1 urllib 2 requests 3 requests html 二 正则表达式 三 数据解析 1 Beautiful Soup 2 lxml 3 selectolax 四 自动化爬虫selenium 五
  • 汉字的区码和位码

    写于2016年12月08日 汉字的区码和位码 由于国标码是四位十六进制 为了便于交流 大家常用的是四位十进制的区位码 所有的国标汉字与符号组成一个94 94的矩阵 在此方阵中 每一行称为一个 区 每一列称为一个 位 因此 这个方阵实际上组成
  • 字典树实现_数据结构与算法之字典树(Golang实现)

    1 字典树 算法描述 trie树的本质 就是利用字符串之间的公共前缀 将重复的前缀合并在一起 时间复杂度 构建O n 查询O k 1 1 1 算法步骤 根节点 什么都不表示 做一个字典比如a z 字母表 没一个节点包含这26个字母的字典表
  • 基于pytorch卷积人脸表情识别--毕业设计

    基于卷积神经网络的人脸表情识别 前言 毕业设计内容介绍 卷积神经网络的设计 卷积网络的模型 卷积池化过程详细说明 第一层卷积池化过程 第二层卷积池化过程 第三层卷积池化过程 全连接层过程 模型的训练过程 卷积与池化原理 模型如何训练 模型的
  • C++字符串全排列(递归法)和(迭代法)以及next_permutation底层原理详解

    目录 前言 next permutation的使用 实现全排列的两种算法 1 递归法 全排列方便理解记忆的方法 作为备用方法 实现代码 无重复元素情况 有重复元素情况 2 迭代法 next permutation底层原理 实现代码 有无重复