全排列,去重全排列的递归与非递归实现

全排列的概念:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。

看到全排列的时候第一反应可能会是那么多情况怎么去写,就跟我们的排列组合一样。

那么要怎样处理全排列呢,我们来看一个例子:123

首先可能的排列有:123,132,  213, 231, 321, 312.我们来看看这个图:

                                                                     

可以很直接地看到,全排列是从第一个元素开始,每个元素都和它后面的元素进行位置的互换从而得到

所以我们就可以这样来实现:

 1 void fullPermutation(int *arrayL, int k, int len) {
 2     //k是开始的下标,len代表数组的长度
 3     if (k== len - 1)
 4         output(arrayL, len);
 5     else {
 6         for (int i = k; i != len; i++) {
 7             swap(arrayL[i], arrayL[k]);
 8             fullPermutation(arrayL, k + 1, len);
 9             swap(arrayL[i], arrayL[k]);
10         }
11     }
12 }

我们从第一个元素开始,先交换两个元素,然后进行一次全排列,然后恢复这两个元素,因为是某个元素和它后面的元素都交换一遍,在交换的时候其他的元素位置应该是没变的。

下面是全部的实现代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <ctime>
 4 
 5 using namespace std;
 6 
 7 int count_ = 0;
 8 
 9 void output(int* arrayL, int len) {
10     for (int i = 0; i != len; i++)
11         printf("%d ", arrayL[i]);
12     printf("
");
13 }
14 
15 void swap(int& a, int& b) {
16     int temp = b;
17     b = a;
18     a = temp;
19 }
20 
21 void fullPermutation(int *arrayL, int k, int len) {
22     //k是开始的下标,len代表数组的长度
23     if (k == len - 1) {
24         output(arrayL, len);
25         count_++;
26     }
27     else {
28         for (int i = k; i != len; i++) {
29             swap(arrayL[i], arrayL[k]);
30             fullPermutation(arrayL, k + 1, len);
31             swap(arrayL[i], arrayL[k]);
32         }
33     }
34 }
35 
36 
37 int main(int argc, char const *argv[])
38 {
39     int start = clock();
40     {
41         int lenOfArray;
42         int *arrayL;
43         printf("Enter the number lrngth of the array: ");
44         scanf("%d", &lenOfArray);
45 
46         arrayL = new int[lenOfArray];
47         printf("Enter the elements of the array: ");
48         for (int i = 0; i != lenOfArray; i++)
49             scanf("%d", &arrayL[i]);
50 
51         fullPermutation(arrayL, 0, lenOfArray);
52 
53         printf("The total seq number is: %d
", count_);
54     }
55     printf("The run time: %.3lfms
",double(clock()-start)/CLOCKS_PER_SEC);
56 
57     return 0;
58 }

因为有时候需要进行全排列的序列是有重复的,那么这个时候如果还按照上面的方法进行全排列的话,就会产生一些重复的序列,比如 1 2 2,按照上面的方法是会得到这样的序列:1 2 2, 1 2 2, 2 1 2, 2 2 1, 2 2 1, 2 1 2. 这样就不正确了。那么应该怎么去掉重复的呢?

可能开始会想到是不是遇到和自己相同的就不交换,因为交换后结果一样?的确,这个交换后结果会一样,但只是这样并不够,如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

我们来看一个序列:1 2 3 2.我们用这个方法,在遇到和前面已经交换过的元素的值相同的元素就不交换,像1 2 3 2,我们在 1 和第一个 2 交换后不再和第二个 2 交换,可能你会说,2 1 3 2 和 2 2 3 1 是不一样的,怎么能不交换呢?因为如果交换的话,变成 2 2 3 1, 但 1 和第一个 2 交换后得到的序列 2 1 3 2 的1 和最后那个 2 交换后又会得到 2 2 3 1,这样就重复了。很明显,像 2 2 3 1 这样的序列会在前面的交换结果的基础上再交换得到:

                                                                                                 

这种和字符串匹配的KMP算法有点像,重复的元素可以在前面交换的结果的基础上再交换的到。这个自己化一下应该就可以得到了。

下面是实现源码:

 1 bool isSwap(int* arrayL, int begin, int end) {
 2     for (int i = begin; i != end; i++)
 3         if (arrayL[i] == arrayL[end])
 4             return false;
 5 
 6     return true;
 7 }
 8 void fullPermutation(int *arrayL, int k, int len) {
 9     //k是开始的下标,len代表数组的长度
10     if (k == len - 1) {
11         output(arrayL, len);
12         count_++;
13     }
14     else {
15         for (int i = k; i != len; i++) {
16             if (isSwap(arrayL, k, i)) {
17                 swap(arrayL[i], arrayL[k]);
18                 fullPermutation(arrayL, k + 1, len);
19                 swap(arrayL[i], arrayL[k]);
20             }
21         }
22     }
23 }

下面是非递归实现:...下次补充。。。

原文地址:https://www.cnblogs.com/xiezhw3/p/3448270.html