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

49 篇文章 8 订阅

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值