全排列及相关扩展算法(一)——基础的回溯递归实现全排列算法

来源:互联网 发布:舒适图标知乎 编辑:程序博客网 时间:2024/06/11 02:24

1.全排列的定义和公式: 从n个数中选取m(m<=n)个数按照一定的顺序进行排成一个列,叫作从n个元素中取m个元素的一个排列。由排列的定义,显然不同的顺序是一个不同的排列。从n个元素中取m个元素的所有排列的个数,称为排列数。从n个元素取出n个元素的一个排列,称为一个全排列。全排列的排列数公式为n!,通过乘法原理可以得到。

2.时间复杂度: n个数(字符、对象)的全排列一共有n!种,所以全排列算法至少时O(n!)的。如果要对全排列进行输出,那么输出的时间要O(n*n!),因为每一个排列都有n个数据。所以实际上,全排列算法对大型的数据是无法处理的,而一般情况下也不会要求我们去遍历一个大型数据的全排列。

3.全排列算法解决思路:假设现有1 2 3 三个数,我们构造全排列的方式为:先确定第一位1,然后可选(2,3)(3,2)作为后序的排列组合,这样以1作为首位的全排列就有(1,2,3)(1,3,2)两种,然后再以2作为第一位,从而构造出(2,1,3)(2,3,1).最后是(3,1,2)(3,2,1)。即求n个数的全排列=先枚举确立一个数,然后求n-1的全排列。这与我们常见的回溯递归法基本一样。当所有的位数都确定完毕,即成为一组完整的排列数,也就是递归的出口。

4.全排列算法代码:

void Permutation(int A[], int m, int n){if (m == n)   {Print(A, n);Count++;}else{for (int i = m; i < n; i++){Swap(A[m], A[i]);Permutation(A, m + 1, n);Swap(A[m], A[i]);}}}

当m==n即所有位数都确定,即为一组完整的排列数,否则枚举确定第m位数(使第m位依次与后面位数交换),递归返回后再回溯还原。

注:Print函数功能为输出A数组前n位,Swap函数为交换两个数,Count为全局变量,记录排列数

void Swap(int &a, int &b){a == b ? 0 : a ^= b ^= a ^= b;}void Print(int A[], int n){for (int i = 0; i < n; i++){printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');}}

外部调用:

int main(){int A[] = { 1,2,3,4 };int n = sizeof(A) / sizeof(A[0]);Permutation(A, 0, n);printf("%d\n", Count);system("pause");return 0;}

5.时间复杂度

此算法可以列出从A数组中第m个元素到第n个元素的所有排列,注意m,n可以在任意位置。算法的复杂度在于for循环和递归,最大的for是n,递归为n-1所以为O(n*n-1*n-2*...1)=O(n!)


6.运行截图






注:函数调用意为:从第2位到第4位(不包括)的全排列,故收影响的排列只有(1,2,3,4,5)(1,2,4,3,5)

由于我们的Print函数是从0位输出到n位的,所以只打印了0-3位,此样例只是辅助说明Permutation函数中m和n参数的作用。


7.参考文档

https://wenku.baidu.com/view/8c79a2facc17552706220880.html


阅读全文
1 0
原创粉丝点击