多种方法实现实现全排列 + 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,因为储存都是二进制储存嘛,所以都可以使用。具体先总结这些,再有发现再补充。

 

 

  

 

posted @ 2017-03-28 22:08  Codorld  阅读(434)  评论(0编辑  收藏  举报