多种方法实现实现全排列 + 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 ...
随机推荐
- javascript中的Strict模式
目录 简介 使用Strict mode strict mode的新特性 强制抛出异常 简化变量的使用 简化arguments 让javascript变得更加安全 保留关键字和function的位置 总 ...
- Python中面向对象的概念
1.语言的分类 1)面向机器 抽象成机器指令,机器容易理解.代表:汇编语言. 2)面向过程 做一件事,排除步骤,第一步做什么,第二步做什么,如果出现A问题,做什么处理,出现b问题,做什么处理.问题规模 ...
- P1028_数的计算(JAVA语言)
题目描述 我们要求找出具有下列性质数的个数(包含输入的自然数n): 先输入一个自然数n(0n≤1000),然后对此自然数按照如下方法进行处理: 不作任何处理; 在它的左边加上一个自然数,但该自然数不能 ...
- Git命令太多记不住?有了这个神器,从此告别输入命令行
一 .SourceTree简介 SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端,拥有可视化界面,容易上手操作.同时它也是Mercurial和Subve ...
- 在B站刷视频多倍速操作
B站多倍数播放 1. 最初天真版 F12 或者笔记本(Fn+F12) console控制台 输入 document.querySelector('video').playbackRate = 4: - ...
- Android学习之启动活动的最佳写法
•开始热身 通过之前的学习,我们现在可以很容易的启动一个活动: 首先通过 Intent 构造出当前的 "意图",然后调用 startActivity() 方法将活动启动起来: ...
- SCIP:构造数据抽象--数据结构中队列与树的解释
现在到了数学抽象中最关键的一步:让我们忘记这些符号所表示的对象.不应该在这里停滞不前,有许多操作可以应用于这些符号,而根本不必考虑它们到底代表着什么东西. --Hermann Weyi <思维的 ...
- elasticsearch之Java调用本地代码
虽然Java虚拟机为开发人员屏蔽了底层的实现细节,使得开发人员不用考虑底层操作系统的差异性.不过在某些应用程序中,还是免不了要直接与底层操作系统上的原生代码进行交互.今天我们就来看一下Java对本地调 ...
- 前端开发面试题 — html篇
正值跳槽的金三银四月,在四月的中旬之际,博主为大家整理了几篇前端面试题,希望不会太迟 1.Doctype作用?标准模式与兼容模式各有什么区别? (1)<!DOCTYPE> 声明位于HTML ...
- ubuntu16.04 安装opencv3.4.0
参考 https://www.cnblogs.com/arkenstone/p/6490017.html https://blog.csdn.net/u013180339/article/detail ...