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

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

  ①比如:对两个数字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. PTE 准备之 Personal introduction

    Task strategies Be prepared! This is your opportunity to give the admissions officers a first impres ...

  2. API管理工具

    开源的api文档管理系统 api文档 php 在项目中,需要协同开发,所以会写许多API文档给其他同事,以前都是写一个简单的TXT文本或Word文档,口口相传,这种方式比较老土了,所以,需要有个api ...

  3. 回顾反射机制Method

    package com.demo.service; public interface SayHello { void sayHello(String name); } 接口实现类 package co ...

  4. 每天自学两小时Python,整理了最详细的学习路线和规

    上次这篇文章每天自学两小时Python,三个月学通月入20K主要是给大家整理了学习资料视频和PDF书籍,很多需要的都关注私信领取了. 很多朋友领取之后都问我教程有了那么应该从哪去开始学习呢,私信太多我 ...

  5. 微信开发者工具导入 wepy 项目“app.json 未找到”报错解决方法

    版本信息: 微信开发者工具:1.03.2101150 wepy:2.0 wepy/cli:6.14.8 问题描述 按照 wepy 文档中的步骤新建项目: $ npm install @wepy/cli ...

  6. STM32内存结构介绍和FreeRTOS内存分配技巧

    这是我第一次使用FreeRTOS构建STM32的项目,踩了好些坑,又发现了我缺乏对于操作系统的内存及其空间的分配的知识,故写下文档记录学习成果. 文章最后要解决的问题是,如何恰当地分配FreeRTOS ...

  7. SyntaxError :invalid syntax Python常见错误

    1.忘记在 if , elif , else , for , while , class ,def 声明末尾添加 ":" 2.使用 = 而不是 ==,= 是赋值操作符而 == 是等 ...

  8. Asp.Net Core 5 REST API 使用 JWT 身份验证 - Step by Step

    翻译自 Mohamad Lawand 2021年1月22日的文章 <Asp Net Core 5 Rest API Authentication with JWT Step by Step> ...

  9. OO_Unit2 多线程电梯总结

    OO_Unit2 多线程电梯总结 相比于Unit1的表达式求导,Unit2的多线程电梯听上去似乎显得更加"高大上".但在完成了3个task的迭代后再回过头去比较这两个单元,我发现其 ...

  10. Lucas(卢卡斯)定理

    Lucas(卢卡斯)定理 定义 若 \(p\) 为质数,且\(a\ge b\ge1\),则有: \[C_{a}^{b}\equiv C_{a/p}^{b/p}\cdot C_{a (mod\,p)}^ ...