算法设计与分析之全排列

合集下载

全排列的几种算法

全排列的几种算法

全排列的⼏种算法全排列,我们⾼中时就学过,数学上很简单,可是⽤计算机的算法实现还是有点味道的,今天我将我碰到的⼏种算法如数奉上,欢迎交流!第⼀种:递归最常见的也是最好理解的⽅法:简单点:⽐如"a" ,"b","c"全排列,可以看做事"a" +"b","c"的全排列及"b"+ "a","c"的全排列及"c" + "a","b"的全排列也就是说,遍历原数组中的每个元素,让剩余的元素全排列,这样就找到规律了。

代码如下:public static void main(String[] args) {char buf[]={'a','b','c','d'};perm(buf,0,buf.length-1);}public static void perm(char[] buf,int start,int end){if(start==end){//当只要求对数组中⼀个字母进⾏全排列时,只要就按该数组输出即可(特殊情况)for(int i=0;i<=end;i++){System.out.print(buf[i]);}System.out.println();}else{//多个字母全排列(普遍情况)for(int i=start;i<=end;i++){//(让指针start分别指向每⼀个数)char temp=buf[start];//交换数组第⼀个元素与后续的元素buf[start]=buf[i];buf[i]=temp;perm(buf,start+1,end);//后续元素递归全排列temp=buf[start];//将交换后的数组还原buf[start]=buf[i];buf[i]=temp;}}}第⼆中⽅法:也是递归,但是和第⼀种有所区别,算法:将数据分为两部分,递归将数据从左侧移右侧实现全排列⽐较抽象,如list abcd,遍历每⼀个数,将每个数放到⼀个新的list中,并将该元素从原list删除,然后将剩下的元素继续遍历每个元素继续放到新的list ⾥,这样,当list的长度为原list长度,或者原list长度为0时打印结果!下⾯是简单的⽰意图:// abcd//bcd a//cd ab//d abc//abcd//c abd//abdc//bd ac//d acb//acbd//b acd//acdb//bc ad ...//acd b ...//abd c ...//abc d ...源代码如下:private static void sort(List datas, List target) {//System.out.println("size="+datas.size());if (datas.size()==0) {for (Object obj : target)System.out.print(obj+" ");System.out.print(" ");return;}for (int i = 0; i < datas.size(); i++) {List newDatas = new ArrayList(datas);List newTarget = new ArrayList(target);newTarget.add(newDatas.get(i));newDatas.remove(i);sort(newDatas, newTarget);}}public static void main(String[] args) {List list = new ArrayList();for(int i=0;i<5;i++){list.add(i+1);}sort(list, new ArrayList());}第三种⽅法:⾮递归直接上代码:public static void main(String[] args) {int[] arr = new int[]{1,2,3,4,5,6};for(int i :arr){System.out.print(i + " ");}System.out.println();int totalnum = 1;while(NextNumber(arr,arr.length)){for(int i :arr){System.out.print(i + " ");}System.out.println();totalnum ++;}System.out.println("Total Num: " + totalnum);}private static Boolean NextNumber(int[] arr, int n){//数组最后⼀个元素位置int lastIndex = n-1;//从右向左确定第⼀个数字(前⾯的数字⽐它⼩)int firstIndex = lastIndex;for(;arr[firstIndex-1]>arr[firstIndex];firstIndex--){if(firstIndex == 1){//已经轮询完毕,此数已经是最⼤的那个数return false;}}//从右向左确定⼀个交换数(此数⽐arr[firstIndex]⼩且⽐arr[firstIndex-1]⼤)int swapIndex = lastIndex;for(;swapIndex > firstIndex;swapIndex--){if(arr[swapIndex] < arr[firstIndex] && arr[swapIndex] > arr[firstIndex-1]){break;}}//交换数字swap(arr,firstIndex-1,swapIndex);//将firstIndex右边的数字排序for(;firstIndex < lastIndex;firstIndex++,lastIndex--){if(arr[firstIndex] > arr[lastIndex]){swap(arr,firstIndex,lastIndex);}}return true;}private static void swap(int[] arr,int i, int j){int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}如果此⽂对你有帮助,请留个⾔,新⼈需要打架的⽀持和⿎励!。

全排列算法

全排列算法

本文为原创,如需转载,请注明作者和出处,谢谢!全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。

现以{1, 2, 3, 4, 5}为例说明如何编写全排列的递归算法。

1、首先看最后两个数4, 5。

它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。

由于一个数的全排列就是其本身,从而得到以上结果。

2、再看后三个数3, 4, 5。

它们的全排列为3 4 5、3 5 4、 4 3 5、 4 53、 53 4、 54 3 六组数。

即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。

因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。

当n = 1时perm(p} = r1。

为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。

算法如下:#include <stdio.h>int n =0;void swap(int *a, int *b){int m;m = *a;*a = *b;*b = m;}void perm(int list[], int k, int m){int i;if(k > m){for(i =0; i <= m; i++) printf("%d ", list[i]);printf("\n");n++;}else{for(i = k; i <= m; i++){swap(&list[k], &list[i]); perm(list, k +1, m); swap(&list[k], &list[i]);}}}int m ain(){int list[] = {1, 2, 3, 4, 5};perm(list, 0, 4);printf("total:%d\n", n);return0;}谁有更高效的递归和非递归算法,请回贴。

全排列生成算法

全排列生成算法

全排列的生成算法对于给左的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。

字典序法按照字典序求下一个排列的算法广例字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321o注意一个全排列可看做一个字符串,字符串可有前缀、后缀/生成给泄全排列的下一个排列所谓一个全排列的下一个排列就是这一个排列与下一个排列之间没有其他的排列。

这就要求这一个排列与下一个排列有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

广例839647521是1—9的排列。

1—9的排列最前而的是123456789,最后而的是987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。

否则找出第一次出现下降的位置。

算法:由P1P2...Pn生成的下一个排列的算法如下:1求j=max{j| Pj-I<pj}2.求|=max{k| Pi-1<Pk }3.交换Pi-1与PI得到P1P2...PI-1 (P i....Pn ),将红色部分顺序逆转,得到结果.例求839647521的下一个排列1.确定i,从左到右两两比较找出后一个数比前一个大的组合,在这里有39 47,然后i 取这些组中最到的位宜号(不是最大的数)在这两组数中7的位置号最大为6,所以i=62.确立I.找岀在i (包括i)后面的所有比i前面那一位大的数的最大的位置号,在此例中7, 5都满足要求,则选5, 5的位置号为7,所以1=73.先将4和5交换,然后将5后的四位数倒转得到结果8396574213 839651247以上算法是在数论课上老师给岀的关于字典序全排列的生成算法,以前也经常要用到全排列生成算法来生成一个全排列对所有的情况进行测试,每次都是现到网上找一个算法,然后直接copy代码,修改一下和自己的程序兼容就行了,也不看是怎么来的,不是我不想看,实在是说的很抽象,那一大堆公式来吓人,一个实例都不给,更有甚者连算法都没有,只是在那里说,想看都看不懂,也没那个耐心取理解那些人写出来的那种让人无法忍受的解释。

高中数学排列组合全排列技巧

高中数学排列组合全排列技巧

高中数学排列组合全排列技巧在高中数学中,排列组合是一个重要的概念和技巧,它涉及到我们日常生活中的很多问题,比如生日礼物的选择、座位的安排等等。

在解决这些问题时,全排列是一种非常常见且有用的方法。

本文将介绍高中数学中全排列的技巧,并通过具体的例题来说明其应用。

全排列是指将一组元素按照一定的顺序进行排列,使得每个元素都出现且只出现一次。

在解决全排列问题时,我们需要注意以下几个关键点。

首先,确定元素的个数。

在解决全排列问题时,我们需要明确给定元素的个数。

例如,有5个不同的字母A、B、C、D、E,我们要求由这5个字母组成的所有三位数的全排列。

其次,确定排列的长度。

在确定元素个数后,我们还需要确定排列的长度。

例如,我们要求由5个字母组成的所有三位数的全排列。

接下来,我们需要确定元素的选择方式。

在全排列中,每个位置上的元素都可以是给定的一组元素中的任意一个。

例如,对于由5个字母组成的所有三位数的全排列,第一个位置上的字母可以是A、B、C、D、E中的任意一个,第二个位置上的字母可以是除去第一个位置上已经选择的字母之外的任意一个,以此类推。

最后,我们需要确定排列的顺序。

在全排列中,我们可以按照字典序、逆序等不同的方式进行排列。

例如,对于由5个字母组成的所有三位数的全排列,我们可以按照字典序进行排列,也可以按照逆序进行排列。

下面通过一个具体的例题来说明全排列的应用。

例题:有4个不同的字母A、B、C、D,要求由这4个字母组成的所有三位数的全排列。

解析:根据题目要求,我们可以确定元素的个数为4,排列的长度为3。

接下来,我们需要确定元素的选择方式。

第一个位置上的字母可以是A、B、C、D中的任意一个,第二个位置上的字母可以是除去第一个位置上已经选择的字母之外的任意一个,第三个位置上的字母可以是除去前两个位置上已经选择的字母之外的任意一个。

最后,我们按照字典序进行排列,得到所有满足条件的三位数的全排列为:ABC, ABD, ACD, BAC, BAD, BCA, BCD, CAB, CAD, CBA, CBD, DAB, DAC, DBA, DBC.通过这个例题,我们可以看出全排列的应用非常广泛。

全排列和对换解析

全排列和对换解析

全排列和对换是组合数学中的重要概念,用于描述排列和组合的规律和性质。

全排列是指将n个不同元素按照一定顺序排列成一个有序序列的方法数。

全排列可以用递归的方式定义,即P(n)表示n个元素的全排列个数,则有:
P(n) = n * P(n-1)
其中,P(1) = 1。

例如,当n=3时,全排列的个数为:
P(3) = 3 * P(2) = 3 * 2 = 6
即有6种不同的排列方式,分别是(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)。

对换是指将两个元素交换位置的一种操作。

对换可以用递归的方式定义,即C(n, k)表示从n 个不同元素中取出k个元素的组合个数,则有:
C(n, k) = n * (C(n-1, k-1) + C(n-1, k))
其中,C(1, 0) = 1,C(n, 0) = 1。

例如,当n=3,k=2时,从3个不同元素中取出2个元素的组合个数为:
C(3, 2) = 3 * (C(2, 1) + C(2, 2)) = 3 * (1 + 2) = 7
即有7种不同的组合方式,分别是(1,2)、(1,3)、(2,3)、(2,1)、(3,1)、(3,2)、(2,1,3)。

需要注意的是,对换是一种破坏性的操作,它会改变原有的排列或组合的顺序。

因此,在计算全排列和组合的个数时,对换应该被视为一种额外的操作,需要在计算结果中加以考虑。

全排列生成算法

全排列生成算法

全排列的生成算法对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。

字典序法按照字典序求下一个排列的算法 /*例字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321。

注意一个全排列可看做一个字符串,字符串可有前缀、后缀。

*/生成给定全排列的下一个排列所谓一个全排列的下一个排列就是这一个排列与下一个排列之间没有其他的排列。

这就要求这一个排列与下一个排列有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

/*例 839647521是1—9的排列。

1—9的排列最前面的是123456789,最后面的是987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。

否则找出第一次出现下降的位置。

算法: 由P1P2…Pn 生成的下一个排列的算法如下:1. 求i=max{j| Pj-1<Pj}2. 求l=max{k| Pi-1<Pk }3. 交换Pi-1 与Pl得到P1P2…Pi-1 (P i....Pn ) , 将红色部分顺序逆转,得到结果. 例求839647521的下一个排列1. 确定i,从左到右两两比较找出后一个数比前一个大的组合,在这里有39 47,然后i 取这些组中最到的位置号(不是最大的数)在这两组数中7的位置号最大为6,所以i=62.确定l,找出在i(包括i)后面的所有比i前面那一位大的数的最大的位置号,在此例中7,5 都满足要求,则选5,5的位置号为7,所以 l=73. 先将4和5交换,然后将5后的四位数倒转得到结果839657421à 839651247以上算法是在数论课上老师给出的关于字典序全排列的生成算法,以前也经常要用到全排列生成算法来生成一个全排列对所有的情况进行测试,每次都是现到网上找一个算法,然后直接copy代码,修改一下和自己的程序兼容就行了,也不看是怎么来的,不是我不想看,实在是说的很抽象,那一大堆公式来吓人,一个实例都不给,更有甚者连算法都没有,只是在那里说,想看都看不懂,也没那个耐心取理解那些人写出来的那种让人无法忍受的解释。

全排列的生成算法

全排列的生成算法

全排列的生成算法全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。

任何n 个字符集的排列都可以与1~n的n个数字的排列一一对应,因此在此就以n个数字的排列为例说明排列的生成法。

n个字符的全体排列之间存在一个确定的线性顺序关系。

所有的排列中除最后一个排列外,都有一个后继;除第一个排列外,都有一个前驱。

每个排列的后继都可以从它的前驱经过最少的变化而得到,全排列的生成算法就是从第一个排列开始逐个生成所有的排列的方法。

全排列的生成法通常有以下几种:字典序法递增进位数制法递减进位数制法邻位交换法递归类算法1.字典序法字典序法中,对于数字1、2、3......n的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。

例如对于5个数字的排列12354和12345,排列12345在前,排列12354在后。

按照这样的规定,5个数字的所有的排列中最前面的是12345,最后面的是54321。

字典序算法如下:设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即j=max{i|pi<pi+1}2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)3)对换pi,pk4)再将pj+1......pk-1pkpk+1pn倒转得到排列p’’=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个下一个排列。

例如839647521是数字1~9的一个排列。

从它生成下一个排列的步骤如下:自右至左找出排列中第一个比右边数字小的数字4 839647521在该数字后的数字中找出比4大的数中最小的一个5 839647521将5与4交换 839657421将7421倒转 839651247所以839647521的下一个排列是839651247。

高中数学排列与组合算法解题思路

高中数学排列与组合算法解题思路

高中数学排列与组合算法解题思路在高中数学中,排列与组合是一个重要的概念,也是解题的常见考点之一。

掌握排列与组合的算法解题思路,对于高中学生来说是非常重要的。

本文将以具体的题目为例,分析和说明排列与组合的考点和解题技巧,帮助读者更好地理解和应用这一知识点。

一、排列问题排列问题是指从给定的元素中选取若干个元素按照一定的顺序排列的问题。

常见的排列问题有全排列、循环排列等。

1. 全排列问题全排列问题是指从给定的元素中选取所有的元素按照一定的顺序排列的问题。

下面以一个具体的例题来说明全排列的解题思路。

例题:有三个不同的字母A、B、C,从中选取两个字母进行排列,列出所有可能的情况。

解题思路:根据排列的定义,我们知道在这个问题中,有3个元素,选取2个进行排列。

根据排列的计算公式,可以得到全排列的个数为3 × 2 = 6。

我们可以使用穷举法列出所有的情况:AB, AC, BA, BC, CA, CB通过这个例题,我们可以看到全排列问题的解题思路是通过穷举法列出所有的情况,根据排列的计算公式计算出全排列的个数。

2. 循环排列问题循环排列问题是指从给定的元素中选取若干个元素按照一定的顺序排列,并且最后一个元素与第一个元素相连的问题。

下面以一个具体的例题来说明循环排列的解题思路。

例题:有三个不同的字母A、B、C,从中选取两个字母进行循环排列,列出所有可能的情况。

解题思路:根据循环排列的定义,我们知道在这个问题中,有3个元素,选取2个进行循环排列。

循环排列的个数等于全排列的个数除以元素个数,即6 ÷ 3 = 2。

我们可以使用穷举法列出所有的情况:AB, BC, CA通过这个例题,我们可以看到循环排列问题的解题思路是先计算出全排列的个数,然后除以元素个数得到循环排列的个数,最后使用穷举法列出所有的情况。

二、组合问题组合问题是指从给定的元素中选取若干个元素进行组合的问题。

常见的组合问题有从n个元素中选取m个元素的组合、有重复元素的组合等。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

为什么需要交换两次
• 举个例子 比如现在数组的数据是 123 算法是这 样的 1和3先交换 变成了321 然后递归 2和1交换 变成了312 然后递归 满足if语句条件输出 然后 在逐层反回 还是 123 只不过这次应该是23交换 了 这样在递归 才有能把所有的组合找出来 如果 你第二次不交换i j 那就是321进入下个循环了 所以会漏掉很多组合
全排列
• 1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和 以5开头的4的全排列。 由于一个数的全排列就是其本身,从而得到以上结果。 2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。 即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的 和3,4的全排列的组合. 从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p- {rn}。 因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。 为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理 后n-1个数的全排列
1. Void Perm(Type list[],int k, int m) 2. {//递归的产生前缀是list[0:k-1]后缀是list[k:m]的全 排列的所有排列 3. if(k==m) {//只剩下一个元素 4. for(int i=0;i<=m;i++) 5. cout<<list[i]; 6. cout<<endl; 7. } 8. else//还有多个元素待排列,递归产生排列 9. for(int i=k;i<=m;i++){ 10. Swap(list[k],list[i]); 11. Perm(list,k+1,m); 12. Swap(list[k],list[i]; 13. } 14.}
4 1 4 2 2 1 1 3 1 2 2 3

对于n个元素 a=(a1a2……ak……an), 设过程range(a, k, n)是求a的第k 到第n个元素的全排列。 算法如下:
procedure range(a,k,n) if k=n then print(a) else for i ← k to n do { a[k] a[i]; procedure range(a,k,n); call range(a,k+1,n); 当k指向最后元素时, a[k] a[i]; 递归终止,输出相应的字符串a } 否则 endif endrange; i从k到n重复执行: 交换ak与ai ; range(a, k+1, n); 交换ak与ai ; endrange;
求n个元素的全排列。
分析:n=1 输出a1; n=2 输出a1 a2; a2 a1; n=3 输出a1 a2 a3; a1 a3 a2; a2 a1 a3; 将(1)中的a1,a2互换位置,得到(2); 将(1)中的a1,a3互换位置,得到(3). a2 a3 a1; a3 a2 a1; a3 a1 a2; 可以用循环重复执行 “交换位置,后跟剩 归纳:n=3时排列的分类 (1)a1类:a1之后跟a2,a3的全排列; 余序列的所有排列”; 对剩余的序列再使用 (2)a2类:a2之后跟a1,a3的全排列; 该方法,直至没有剩 (3)a3类:a3之后跟a2,a1的全排列。 余序列——递归调用
两个例子
• 123的全排列 --首先遍历元素,然后把遍历到的每一个元素都和第一个元 素交换 第一个和第一个交换 就是1和1交换 最后还是123 那么就形成了 123 213 321 这样的3组 (交换后 再换回来 还原成123 因为后面的交换都是在123的 基础上交换的 所以swap要写2次) -检查每一组除了第一个之外的剩余元素, 如果这些元素个 数是2,那么就对这剩下的2个元素全排列 就是123 132 , 213 231 , 321 312 2个元素的全排列很简单 就是 把这2个元素交换位置就OK)
由排列组合的知识可知,n个元 素的全排列共有n!种。 n!可分解为n*(n-1)!种,而 (n-1)!又分解为(n-1)(n-2)!种, 依次类推。
若用一个数组a[n]来保存1~n n=3 1 2 3 之间的n个自然数,对于i 1 3 2 =1~n,每次使a[1]同a[i]交换 2 1 3 2 3 1 后,对a[2]~a[n]中的n-1个元 3 2 1 3 1 2 素进行全排列,然后再交换 a[1]与a[i]的值,使它恢复为 此次排列前的状态; 同样,对于a[3]~a[n]区间内 的n-2个元素进行全排列,然 后再把交换的元素交换回来; 依次类推,直到对a[n]进行全 排列时,输出整个数组的值, 即得到一种排列结果。
n=4 1 2 1 2 1 3 1 3 1 4 1 4 2 1 2 1 2 3 2 3 2 4 2 4
3 4 2 4 3 2 3 4 1 4 3 1
4 3 4 2 2 3 4 3 4 1 1 3
3 3 3 3 3 3 4 4 4 4 4 4
2 2 1 1 4 4 2 2 3 3 1 1
1 4 2 4 1 2 3 1 2 1 3 2
两个例子
• 1234的全排列 --首先遍历元素,然后把遍历到的每一个元素都和第一个 元素交换 那么就形成了 1234 2134 3214 4231 这样的4组 -检查每一组除了第一个之外的剩余元素, 如1234剩余的 是234,发现是3个元素 那么问题就转换为求234的全排列了 接下来也是一样 问题转换为求134, 214, 231的全排列 像这样 总是对除了第一个之外的元素全排列, 每次元素 的个数都在减少一个,求N个元素全排列最终就变成求2的 元素的全排列了
相关文档
最新文档