全排列:所有不同顺序的元素组组成的一个集合。这里使用使用递归实现全排列。

  使用递归算算法呢,首先我们先找一下结束的条件:我们要对一组元素(这里使用数字举例)实现全排列,临界条件就是递归到只有一个元素的时候就输出。

  ①比如:对两个数字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调用标准函数库函数的简述的更多相关文章

  1. 用 Python 排序数据的多种方法

    用 Python 排序数据的多种方法 目录 [Python HOWTOs系列]排序 Python 列表有内置就地排序的方法 list.sort(),此外还有一个内置的 sorted() 函数将一个可迭 ...

  2. js判断移动端是否安装某款app的多种方法

    本文实例讲解了js判断移动端是否安装某款app的多种方法,分享给大家供大家参考,具体内容如下 第一种方法: 一:判断是那种设备 ? || u.indexOf(; //android终端或者uc浏览器 ...

  3. Gradle学习系列之二——创建Task的多种方法

    在本系列的上篇文章中,我们讲到了Gradle入门,在本篇文章中我们将讲到创建Task的多种方法. 请通过以下方式下载本系列文章的Github示例代码: git clone https://github ...

  4. C# Winform窗口之间传值的多种方法浅析(转)

    摘要http://www.jb51.net/article/63837.htm 这篇文章主要介绍了C# Winform窗口之间传值的多种方法浅析,本文起讲解了通过构造器传值.通过属性传递.通过事件携带 ...

  5. 使用msiexec.exe绕过应用程序白名单(多种方法)

    0x00 前言 在我们之前的文章中,我们讨论了“Windows Applocker策略 - 初学者指南”,因为它们为应用程序控制策略定义了AppLocker规则,以及如何使用它们.但今天您将学习如何绕 ...

  6. 使用mshta.exe绕过应用程序白名单(多种方法)

      0x00 简介 很长一段时间以来,HTA文件一直被web攻击或在野恶意软件下载程序用作恶意程序的一部分.HTA文件在网络安全领域内广为人知,从红队和蓝队的角度来看,它是绕过应用程序白名单有价值的“ ...

  7. 使用regsrv32.exe绕过应用程序白名单(多种方法)

    0x00 regsvr简介 regsvr32表示Microsoft注册服务.它是Windows的命令行实用工具.虽然regsvr32有时会导致问题出现,但它是Windows系统文件中的一个重要文件.该 ...

  8. ArcGIS API For Silverlight使用在线地图的多种方法总结

    引自:http://www.cnblogs.com/meimao5211/p/3283969.html ArcGIS API For Silverlight使用在线地图的多种方法总结 本人也正在学习A ...

  9. 创建Task的多种方法

    Gradle的Project从本质上说只是含有多个Task的容器,一个Task与Ant的Target相似,表示一个逻辑上的执行单元. 我们可以通过多种方式定义Task,所有的Task都存放在Proje ...

随机推荐

  1. 如何对shell脚本中斜杠进行转义?

    1.在编写shell脚本时,经常会遇到对某个路径进行替换,而路径中包含斜杠(/),此时我们就需要对路径中涉及的斜杠进行转义,否则执行失败.具体示例如下: 需求描述: 将sjk目录下的test文件中的p ...

  2. IPFS挖矿必须要托管吗?

    IPFS 本质上只是一个人人使用的协议,而 Filecoin 是 IPFS 的激励层,大家平时说的 IPFS 挖矿,其实就是挖 Filecoin.而提到IPFS 就不得不说到矿机托管的问题. 点击了解 ...

  3. unable to read askpass response from 'C:\Users\wxy\.IntelliJIdea2019.1\system\tmp\intellij-git-askpass.bat' bash: /dev/tty: No such device or address failed to execute prompt script (exit code 1)

    解决方法:

  4. SpringBoot中整合Redis、Ehcache使用配置切换 并且整合到Shiro中

    在SpringBoot中Shiro缓存使用Redis.Ehcache实现的两种方式实例 SpringBoot 中配置redis作为session 缓存器. 让shiro引用 本文是建立在你是使用这sh ...

  5. (原创)高DPI适配经验系列:(一)缩放比例与DPI对应关系

    一.前言 当下,2K分辨率已成为主流标配,3K.4K也已经广泛应用. 在屏幕尺寸不变的情况下,高分辨率也就意味着高DPI,对于桌面程序而言,除了先天就支持高DPI的框架外(如UWP.Electron等 ...

  6. 「HTML+CSS」--自定义加载动画【009】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  7. python基础(十):集合的使用(上)

    集合的作用 去重:把一个列表变成集合,就自动去重了. 关系测试:测试两组数据之前的交集.差集.并集等关系. 集合的特征 集合使用 set 表示: 集合也使用{ }表示, 与字典不同的是:字典中存储的是 ...

  8. OO第一单元总结与反思

    OO第一单元总结与反思 目录 OO第一单元总结与反思 摘要 第一次作业 本次作业UML类图 本次作业度量分析 第二次作业 本次作业的UML类图 本次作业的度量分析 第三次作业 本次作业的UML类图: ...

  9. BUAAOO第一单元代码分析

    1.HomeWork1 思路 一个主类用于字符串得操作, 一个Poly类用于对一个多项式进行抽象,用Arraylist来对term进行封装.内部含有求导方法,添加并合并同类项的方法,toString方 ...

  10. OOUnit1Summary

    一.前三次作业内容分析 前言 第一单元的作业以表达式求导为主题,分三次要求逐步增加,难度逐步提高.这三次作业下来,本人既有收获,也有遗憾,因此通过接下来的内容对我这三次作业进行分析和总结,希望能能为我 ...