多种方法实现实现全排列 + sort调用标准函数库函数的简述
全排列:所有不同顺序的元素组组成的一个集合。这里使用使用递归实现全排列。
使用递归算算法呢,首先我们先找一下结束的条件:我们要对一组元素(这里使用数字举例)实现全排列,临界条件就是递归到只有一个元素的时候就输出。
①比如:对两个数字1、2进行全排列,当我们固定数字1在第一个位置的时候,那么第二个位置就只可以固定数字2了。接着第一个位置还可以固定数字2,自然第二个位置就只可以固定数字1了。
②三个数1、2、3的时候,我们只需要在第一个位置分别固定数字1、2、3,第二个位置就只可以固定除去第一个位置固定的那个数字的另外的两个数字,接着这个思路,第三个位置就只剩下一个数字了,输出即可。
③这里可以推断出全排列的过程就是让集合中的n个数和第一个数交换之后对剩下的n-1个数进行全排列(继续调用自身函数实现n-1个数和第二个数交换之后对剩下的n-2个数进行全排列(...))。
思路已经出来了,下面进行代码实现。
1 #include <stdio.h>
2
3 int cnt = 0; // 计数使用,其最后的大小就是元素个数的阶乘
4
5 void swap(int *a, int *b) {
6 int m;
7 m = *a;
8 *a = *b;
9 *b = m;
10 }
11
12 void perm(int list[], int k, int m) {
13 int i;
14 if(k > m) {
15 for(i = 0; i <= m; i++)
16 printf("%d ", list[i]);
17 printf("\n");
18 cnt++;
19 } else {
20 for(i = k; i <= m; i++) {
21 swap(&list[k], &list[i]);
22 perm(list, k + 1, m);
23 swap(&list[k], &list[i]);
24 }
25 }
26 }
27
28 int main() {
29 int list[] = {1, 2, 3, 4, 5}; // 在进行全排列之前也可以进行一次排序
30 perm(list, 0, 4);
31 printf("total:%d\n", cnt);
32 return 0;
33 }
34
35 // 如果使用C++写的话,这里排序和交换、
36 // 甚至全排列都可以使用STL实现,下文中介绍。
全排列-C语言递归实现
使用以上代码实现1、2、3、4四个数字的全排列的时候,下面是只有固定第一个位置为1的时候的全排列
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 3 2
1 4 2 3
①前四行还是按照升序的顺序,第五行和第六行就不是了,如果对1~6进行全排列的时候会更明显。这是为什么呢???
②不知道你们有没有发现,三个数字的时候没有出现这种错误,四个时候只是最后两个。再进行细的划分:
③四个数的时候,当第一个数固定数字1的时候,第二个位置固定数字2,第三个位置首先固定数字3,第四个位置固定4,输出1 2 3 4;
④然后递归回来第三个位置固定4,第四个位置固定3(递归是倒着的,最后两个数字交换位置),输出1 2 4 3;
⑤继续递归,第二个位置固定3,第三个位置首先固定2,因为数字2 3交换位置了,第四个位置固定4,输出1 3 2 4;
⑥继续递归,第三个位置固定4(最后两个数字交换位置),最后一个固定2,输出1 3 4 2;
⑦继续... 第二个位置该固定4了,那么2就被换到4原来的那个位置了。所以就输出了1 4 3 2;
⑧然后交换最后两个,输出1 4 2 3;
⑨继续固定第一个位置为数字2重复以上八个步骤。
所以在超过四个元素的时候肯定会出现上面的情况(大的数字4跳跃<越过3>到前面2的时候),是不可能直接得到按照字典序的结果的。也可以使用数组来进行储存并处理再输出,但是那样太麻烦了。还有一种方法就是另外定义一个数组来记录上一次的位置。
具体实现思想:定义一个数组用来填写全排列后的结果,另外定义的数组来记录上一次填写到的位置,比如0123,第一次递归填写到0123这种情况,然后递归到2的位置填写3,3的位置填写2,。具体实现见代码。
1 #include <iostream>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5
6 bool used[100];
7 int p[100];
8
9 void perm (int pos, int n) {
10 if (pos == n) {
11 for (int i = 0; i < n; ++i) {
12 cout << p[i] << " ";
13 }
14 cout << endl;
15 return;
16 }
17 for (int i = 0; i < n; ++i) {
18 if (!used[i]) {
19 p[pos] = i;
20 // i已经使用过了,所以要标记为true
21 used[i] = true;
22 perm(pos+1, n);
23 // 递归回来之后要把标志复位
24 used[i] = false;
25 }
26 }
27 return;
28 }
29
30 int main () {
31 int n;
32 cin >> n;
33 perm(0, n);
34 return 0;
35 }
使用数组标记方法递归实现全排列
当然方便的C++还在STL中实现了全排列的函数,我们只需要记住会用就可以了。下面介绍使用C++标准函数库STL中的函数进行全排列。
先介绍三个C++的函数,sort()--排序函数、next_permutation(); 和 prev_permutation();猜名字估计都可以看出来,是字典上升序和下降序进行全排列的函数。例如:升序就是如果字符串为1234的话第二个就是1243;如果第一个是1423的话,第二个就是1432。值得注意的是:这两个函数的结束标志是标准升序(123456)和标准降序(654321),如果是在中间开始全排列的,会有一部分排不出来,所以建议如果不能保证标准升序或降序输入输入的情况下,先排序再进行全排列。这里先使用next_permutation()这个函数举例子。
bool next_permutation( iterator start, iterator end );
使用方法:传入两个形参类型是迭代器iterator类,返回值类型为bool。
1 #include <iostream>
2 #include <string>
3 #include <algorithm>
4 using namespace std;
5
6 int main() {
7 string str;
8 cin >> str;
9 sort(str.begin(), str.end()); // 为了易于观察,这里排一下序(默认升序)
10 while (next_permutation(str.begin(), str.end()))
11 cout << str << endl;
12 return 0;
13 }
C++标准函数库STL实现全排列
上面的代码全部都是使用的C++标准函数库里面的函数,但是由于C++较于C语言来说封装抽象度较高,所以在处理大数据的时候时间会很长。又因为C++完全包含C语言的语法的,所以我们这里在处理大数据的时候可以结合C++和C语言的语法,就像下面这样写:
1 #include <cstdio>
2 #include <algorithm>
3 #include <cstring>
4 using namespace std;
5
6 const long long MAX = 1e13 + 10;
7
8 char str[MAX];
9
10 int main()
11 {
12 int length;
13 gets(str);
14 length = strlen(str);
15 sort(str, str + length);
16 puts(str);
17 while (next_permutation(str, str + length))
18 {
19 puts(str);
20 }
21 return 0;
22 }
C++、C语言混合实现全排列
上面的两个例子中使用到了next_permutation()这个函数,结合的sort函数,sort默认的是升序。如果想使用prev_permutation()的话,可以使用sort进行降序排一下序。只需要自己另外多写一个函数就可以了。这个是之前的一个简单写了几个排序的博文,下面简单串解一下sort的降序使用方法:再调用sort函数的时候添加一个参数(函数指针)就可以了。具体看代码:
1 #include <algorithm>
2 #include <iostream>
3 using namespace std;
4
5 bool compare(int a, int b) {
6 return a < b; //升序排列,如果改为return a>b,则为降序
7 }
8
9 int main() {
10 int a[10000], n;
11 cin >> n;
12 for(int i = 0; i < n; i++)
13 cin >> a[i];
14
15 sort(a, a+n, compare);
16
17 for(int i = 0; i < n; i++)
18 cout << a[i] << endl;
19 return 0;
20 }
sort实现降序排序
sort函数的参数:前两个参数为要排序的连续空间的首尾地址,第三个参数就是排序的规则(其中相邻的两个元素要满足那个关系)。
也可以使用闭包的方式来写这个函数,虽然写法简单了,但是好像从速度上来说减慢了(只是个人猜测,并没有经过测试)。
1 #include <iostream>
2 #include <algorithm>
3 using namespace std;
4
5 int main () {
6 string str;
7 cin >> str;
8 sort (str.begin(), str.end(), [](const char a, const char b){return a > b;});
9 cout << str << endl;
10 return 0;
11 }
必要实现sort函数的函数指针
需要注意的一点是:如果使用g++命令行编译的话,需要添加参数-std=c++11
即: g++ -std=c++11 Test.cpp -o a&&a
还有一种调用C++函数库的方法;就是使用less<数据类型>(); // 从小到大排序,greater<数据类型>() //从大到小排序。
1 #include <iostream>
2 #include <algorithm>
3 using namespace std;
4
5 bool compare (int a, int b) {
6 return a > b;
7 }
8 int main () {
9 int a[] = {1, 2, 3, 4, 5};
10 sort (a, a+5, greater<int>()); // 从大到小排序;换做less<int>()就是从小到大
11 for (int i = 0; i < 5; ++i) {
12 cout << a[i] << " ";
13 }
14 cout << endl;
15
16 while (prev_permutation(a, a+5)) {
17 for (int i = 0; i < 5; ++i) {
18 cout << a[i] << " ";
19 }
20 cout << endl;
21 }
22 return 0;
23 }
使用less或greater代替排序参数
鄙人才疏学浅,使用less和greater处理string的时候总是报错,如果是string的可以使用传入函数指针的方法,比较函数参数类型可以使用char或者int,因为储存都是二进制储存嘛,所以都可以使用。具体先总结这些,再有发现再补充。
多种方法实现实现全排列 + sort调用标准函数库函数的简述的更多相关文章
- 用 Python 排序数据的多种方法
用 Python 排序数据的多种方法 目录 [Python HOWTOs系列]排序 Python 列表有内置就地排序的方法 list.sort(),此外还有一个内置的 sorted() 函数将一个可迭 ...
- js判断移动端是否安装某款app的多种方法
本文实例讲解了js判断移动端是否安装某款app的多种方法,分享给大家供大家参考,具体内容如下 第一种方法: 一:判断是那种设备 ? || u.indexOf(; //android终端或者uc浏览器 ...
- Gradle学习系列之二——创建Task的多种方法
在本系列的上篇文章中,我们讲到了Gradle入门,在本篇文章中我们将讲到创建Task的多种方法. 请通过以下方式下载本系列文章的Github示例代码: git clone https://github ...
- C# Winform窗口之间传值的多种方法浅析(转)
摘要http://www.jb51.net/article/63837.htm 这篇文章主要介绍了C# Winform窗口之间传值的多种方法浅析,本文起讲解了通过构造器传值.通过属性传递.通过事件携带 ...
- 使用msiexec.exe绕过应用程序白名单(多种方法)
0x00 前言 在我们之前的文章中,我们讨论了“Windows Applocker策略 - 初学者指南”,因为它们为应用程序控制策略定义了AppLocker规则,以及如何使用它们.但今天您将学习如何绕 ...
- 使用mshta.exe绕过应用程序白名单(多种方法)
0x00 简介 很长一段时间以来,HTA文件一直被web攻击或在野恶意软件下载程序用作恶意程序的一部分.HTA文件在网络安全领域内广为人知,从红队和蓝队的角度来看,它是绕过应用程序白名单有价值的“ ...
- 使用regsrv32.exe绕过应用程序白名单(多种方法)
0x00 regsvr简介 regsvr32表示Microsoft注册服务.它是Windows的命令行实用工具.虽然regsvr32有时会导致问题出现,但它是Windows系统文件中的一个重要文件.该 ...
- ArcGIS API For Silverlight使用在线地图的多种方法总结
引自:http://www.cnblogs.com/meimao5211/p/3283969.html ArcGIS API For Silverlight使用在线地图的多种方法总结 本人也正在学习A ...
- 创建Task的多种方法
Gradle的Project从本质上说只是含有多个Task的容器,一个Task与Ant的Target相似,表示一个逻辑上的执行单元. 我们可以通过多种方式定义Task,所有的Task都存放在Proje ...
随机推荐
- 关于go中并发的初步理解
1.一些概念的介绍: 概念 描述 进程 在内存中的程序.有自己独立的独占的虚拟 CPU .虚拟的 Memory.虚拟的 IO devices. (1) 每一进程占用独立的地址空间. 此处的地址空间包括 ...
- 杨辉三角的实现(Java)
杨辉三角的实现 一.什么是杨辉三角 杨辉三角是二项式系数在三角形中的一种几何排列.每个数等于它上方两数之和.每行数字左右对称,由1开始逐渐变大.第n行的数字有n项.前n行共[(1+n)n]/2 个数. ...
- 想了解FlinkX-Oracle Logminer?那就不要错过这篇文章
FlinkX-Oracle Logminer模块是FlinkX基于Logminer对Oracle重做日志进行实时采集分析,可对Oracle进行实时同步也可以通过指定SCN或者时间戳从某个节点进行同步, ...
- IDA 创建本地类型
在IDA中我们常常使用 shift+F9打开结构体视图,ins 创建结构体,但操作有些繁琐. 我们可以在View-->Open Subviews-->Local Types(视图--> ...
- 在SSM框架中如何将图片上传到数据库中
今天我们来看看SSM中如何将图片转换成二进制,最后传入到自己的数据库中,好了,废话不多说,我们开始今天的学习,我这里用的编辑器是IDEA 1.导入图片上传需要的jar依赖包 1 <depende ...
- 实践解析丨Rust 内置 trait:PartialEq 和 Eq
摘要:Rust 在很多地方使用了 traits, 从非常浅显的操作符重载, 到 Send, Sync 这种非常微妙的特性. Rust 在很多地方使用了 traits, 从非常浅显的操作符重载, 到 S ...
- 【linux】驱动-5-驱动框架分层分离&实战
目录 前言 5. 分离分层 5.1 回顾-设备驱动实现 5.2 分离分层 5.3 设备 5.4 驱动 5.5 系统,模块 5.6 Makefile 参考: 前言 5. 分离分层 本章节记录实现LED驱 ...
- webpack核心模块tapable源码解析
上一篇文章我写了tapable的基本用法,我们知道他是一个增强版版的发布订阅模式,本文想来学习下他的源码.tapable的源码我读了一下,发现他的抽象程度比较高,直接扎进去反而会让人云里雾里的,所以本 ...
- CVE-2020-1472 Zerologon
CVE-2020-1472 Zerologon 漏洞简介 CVE-2020-1472是继MS17010之后一个比较好用的内网提权漏洞,影响Windows Server 2008R 2至Windows ...
- Qt信号槽源码剖析(二)
大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的基本概念.元对象编译器.示例代码以及Qt宏:今天接着深入分析,进入Qt信号槽源码剖析系列的第二节视频. Qt信号槽的宏 ...