全排列生成算法
递归法
普通递归,新加入的数字在中间穿梭
递归算法存在爆栈的问题
利用深度优先搜索DFS的递归方式。
1-n的排列的一一对应,存在一种顺序关系。
全排列与深度优先搜索:https://blog.csdn.net/u013238646/article/details/45417809
#include<stdio.h>
int n,a[10],book[10];//特别说明c语言全局变量没有赋值默认为 0,无需再次初始化;
void dfs(int step)//step 表示当前在第几个位置
{
int i;
if(step==n+1)//如果step==n+1表示前n个数字已经放好
{
//输出一种全排列
for(i=1;i<=n;i++)
printf("%d",a[i]);
printf("\n");
return;
}
//每次搜索都从1-n 一一尝试
for(i=1;i<=n;i++)
{
if(book[i]==0)//判断次数字是否用过
{
a[step]=i;//存储当前位置的数字,以便满足条件输出
book[i]=1;//当前数字已用过,改变标志,以防重用
dfs(step+1);//放好当前位置数字之后,安排下一个数字
book[i]=0;//回溯,当满足一种全排列后,进行下一种尝试
}
}
return ;
}
int main()
{
scanf("%d",&n);//输入只能为1-9之间的整数,表示1-n的全排列
dfs(1);//从第一个位置开始
return 0;
}
字典序法
排列是有续的,除去最后一个排列中,都有前驱。除去第一个排列,都有后继。
所谓一个的下一个,就是这一个与下一个之间没有其他的。
一个字符串与下一个有尽可能长的共同前缀,也即变化尽可能限制在尽可能短的后缀上。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
如果123的字典序全排列为:123 132 213 231 312 321
按字典序排列寻找下一个全排列的算法:由此非递归求出下一个全排列
从后往前扫描,如果一个序列从右向左都是增加的,就不存在下一个。如321
要找第一次出现下降的位置。要找到,比下降位置那个大的 中 最小的 那个换,交换完成以后再改变后缀的排列顺序使后缀字典序最小
SJT算法
全排列的生成
1234
数字具有移动的方向,每次交换相邻的数字,最大的能移动的数字发生交换。
4123—> 4132—> 132 4
可移动数:如果该数的方向 指向 的相邻数比概述小的话 则称该数是可移动数。1永远不可移动,n除了指向边界外都可以移动。
好处:模拟了递归的过程,但是不需要写递归
对于三种算法,虽然都是无重无漏,但是生成的顺序不相同。
对于算法的时间复杂度。复杂度很高。应为有n!个
递归的时间最快,但存在一定的内存风险。 根据需要进行修改。
问:如果不求所有全排列。
能否仅仅求出局部的序列。
83647521在字典序中是第几个排列:
77!+26!+45!+24!+33!+22!+1*1!
第一位数字有7654321七个都比他小,后面那些元素都是全排列7!
第二位数字 可选12 第三位数字可选 1245 系数为该位右边比他小的数字的个数(可以用的)。
什么叫做序号?
序号是先于此排列的排列的个数。
康托展开-字典序法
康托展开,利用阶乘表示数字。 康托集合论的奠基人。
中介数:序号与排列之间的一一对应的关系。中介数也就是康托展开的一种系数
中介数特点:记录第i位右边比当前数字小的数字个数,即记录了一种排列结构。
由中介数可以推导出排列
排列——序号——中介数 完全一一对应。
注意中介数的每一位 ki的最大值是n-i。序号是十进制数。
中介数并非一个等进制数。是一个递增进位制数法。
https://leetcode.com/problems/permutation-sequence/
https://leetcode.com/problems/next-permutation/
如何求康托展开的系数(即由序号转换为递增进制数)
逐一除去阶乘,取模。 模值就是进位值。
如何由序号转换为康托展开的系数需要回溯已经使用过的。
由此可知在字典序法中由中介数求排列比较麻烦,我们重新定义中介数。得到递增进位制数法
递增进位制数法
中介数的定义:ai记录数字i右边比i小的个数有多少个。
由递增进位制数法的中介数求排列:
先从高位开始放。(从大的那个数开始放下去)一次扫描就可以找到相应的位置
eg 839647521—849617523 是一个相邻的排列 邻域性质比较差
递减进位制数法
对于递增进制数,由于最低位进制太小,容易很频繁的进位。造成下一个排列与前一个排列之间的关联性没有那么紧密。 最低一位逢n进一。
递减进位制数法与递增进位制数法是一个翻转的关系。
递减进位制数,下一个排列有比较好的邻域性。更好地方法有邻位兑换法。
在进位不频繁,中介数不进位的情况下,只要把最大数和左边相邻数对换一下即可求得下一个排列。
如何求给定一个排列后面的某个排列:
由原排列求得原中介数,在通过递增递减进位制数法加减法求得新中介数,在转换为新排列。
组合的生成
排列是有序的,组合是无序的。