搜档网
当前位置:搜档网 › 全排列的生成算法

全排列的生成算法

全排列的生成算法
全排列的生成算法

精品文档

全排列的生成算法全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。任何n个字符集的排列都可以与1?n的n个数字的排列一一对应,因此

在此就以n 个数字的排列为例说明排列的生成法。

n 个字符的全体排列之间存在一个确定的线性顺序关系。所有的排列中除最后一个排列外,都有一个后继;除第一个排列外,都有一个前驱。每个排列的后继都可以从它的

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

全排列的生成法通常有以下几种:字典序法递增进位数制法递减进位数制法邻位交换法

递归类算法

1. 字典序法

字典序法中,对于数字1、2、3 ......n 的排列,不同排列的先后关系是从左到右逐

个比较对应的数字的先后来决定的。例如对于5个数字的排列 1 2354和12345,排列12345 在前,排列12354 在后。按照这样的规定, 5 个数字的所有的排列中最前面的是 1 2345 ,最后面的是54321 。

字典序算法如下:

设P是1?n的一个全排列:

p=p1p2 .... pn=p1p2 ...... pj-1pjpj+1 ..... p k-1pkpk+1 .... pn

1 )从排列的右端开始,找出第一个比右边数字小的数字的序号j(j 从左端开始计算),即j=max{i|pi

2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即k=max{i|pi>pj}

(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)

3)对换pi ,pk

4 )再将pj+1 ................. pk-1pkpk+1pn 倒转得到排列p ' '

=p1p2 .... p j-1pjpn ..... pk+1pkpk-1 .... p j+1 ,这就是排列p 的下一个下一个排列。

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

将7421 倒转839651247 所以839647521 的下一个排列是839651 247 。

2. 递增进位数制法

在递增进位制数法中,从一个排列求另一个排列需要用到中介数。如果用ki 表示排

列p1p2...pi...pn 中元素pi 的右边比pi 小的数的个数,则排列的中介数就是对应的排

列k1 .... ki ...... kn-1 。

例如排列839647521 的中介数是72642321 ,7、2、6、..... 分别是排列中数字8、3、

1欢迎。下载

精品文档

9、.. 的右边比它小的数字个数。

中介数是计算排列的中间环节。已知一个排列,要求下一个排列,首先确定其中介数,一个排列的后继,其中介数是原排列中介数加 1 ,需要注意的是,如果中介数的末位

kn-1+1=2 ,则要向前进位,一般情形,如果ki+1=n-i+1 ,则要进位,这就是所谓的递增进位制。

例如排列839647521 的中介数是72642321 ,则下一个排列的中介数是

67342221+1=67342300 (因为1+1=2,所以向前进位,2+1=3,又发生进位,所以下一个中介数是67342300 )。

得到中介数后,可根据它还原对应得排列。

算法如下:

中介数k1 、k2 、、kn-1 的各位数字顺序表示排列中的数字n、n-1 、... 、2

在排列中距右端的的空位数,因此,要按k1、k2、... 、kn-1 的值从右向左确定n、

n-1 、 . 、2 的位置,并逐个放置在排列中:i 放在右起的ki+1 位,如果某位已放有

数字,则该位置不算在内,最后一个空位放1。

因此从67342300 可得到排列849617523,它就是839647521 的后一个排列。因为9 最先放置,k1=6,9 放在右起第7 位,空出 6 个空位,然后是放8,k2=7,8 放在右起第8 位,但

9 占用一位,故8 应放在右起第9 位,余类推。

3. 递减进位制数法

在递增进位制数法中,中介数的最低位是逢 2 进1,进位频繁,这是一个缺点。把递

增进位制数翻转, 就得到递减进位制数。

839647521 的中介数是67342221(k1k2...kn-1) ,倒转成为12224376(kn-1...k2k1) ,这是递减进位制数的中介数:ki(i=n-1,n-2,...,2) 位逢i 向ki-1 位进1。给定排列p,p 的下一个排列的中介数定义为p 的中介数加1。例如p=839647521,p 的中介数为12224376 ,p的下一个排

列的中介数为12224376+1=12224377,由此得到p的下一个排列为893647521。

给定中介数,可用与递增进位制数法类似的方法还原出排列。但在递减进位制数中,可以不先计算中介数就直接从一个排列求出下一个排列。具体算法如下:

1 )如果p(i)=n 且i<>n ,则p(i) 与p(i-1) 交换

2)如果p(n)=n ,则找出一个连续递减序列9、8、、i ,将其从排列左端删除,

再以相反顺序加在排列右端,然后将i-1 与左边的数字交换

例如p=893647521 的下一个排列是983647521。求983647521 的下一个排列时,因为9在最左边且第 2 位为8,第3 位不是7,所以将8 和9从小到大排于最右端364752189, 再将7与其左方数字对调得到983647521 的下一个排列是367452189。又例如求987635421 的下一个排列,只需要将9876 从小到大排到最右端并将 5 与其左方数字3 对调,得到534216789。

4. 邻位对换法

邻位对换法中下一个排列总是上一个排列某相邻两位对换得到的。以4个元素的排列

为例,将最后的元素4逐次与前面的元素交换,可以生成4个新排列:

1 2 3 4 1 2 4 3 1 4 2 3 4 1 2 3

然后将最后一个排列的末尾的两个元素交换,再逐次将排头的4与其后的元素交换,

又生成四个新排列:

4 1 3 2 1 4 3 2 1 3 4 2 1 3 2 4

精品文档

再将最后一个排列的末尾的两个元素交换,将 4 从后往前移:

3 1 2

4 3 1 4 2 3 4 1 2 4 3 1 2 如此

循环既可求出全部排列。

5. 元素增值法(n 进制法)

1)从原始排列p=p1p2……pn 开始,第n位加n-1,如果该位的值超过n,则将它除以n,用余数取代该位,并进位(将第n-1位加1)

2)再按同样方法处理n-1 位, n-2 位, ..... ,直至不再发生进位为止,处理完一个

排列就产生了一个新的排列

3)将其中有相同元素的排列去掉

4)当第一个元素的值>n则结束

以3个数1、2、3的排列为例:原始排列是 1 2 3,从它开始,第3个元素是3,3+2=5,5 Mod 3=2 ,第2 个元素是2,2+1=3,所以新排列是1 3 2 。通过元素增值,顺序产生的排列是: 1 2 3 ,1 3 2 ,2 1 1 ,2 1 3 ,2 2 2 ,2 3 1 ,2 3 3 ,3 1 2 ,3 2 1 有下划线的排列中存在重复元素,丢弃,余下的就是全部排列。

6. 递归类算法

全排列的生成方法用递归方式描述比较简洁,实现的方法也有多种。

1 )回溯法

回溯法通常是构造一颗生成树。以3 个元素为例;树的节点有个数据,可取值是1、2、3。如果某个为0,则表示尚未取值。

初始状态是(0,0,0),第1 个元素值可以分别挑选1,2,3,因此扩展出3个子结点。用相同方法找出这些结点的第2个元素的可能值,如此反复进行,一旦出现新结点的3个数据全非零,那就找到了一种全排列方案。当尝试了所有可能方案,即获得了问题的解答。

2)递归算法

如果用P 表示n 个元素的排列,而Pi 表示不包含元素i 的排列,(i)Pi 表示在排列Pi 前加上前缀i 的排列,那么,n 个元素的排列可递归定义为:

如果n=1,则排列P只有一个元素i

如果n>1,则排列P由排列(i)Pi构成(i=1、2、?…、n-1 )。

根据定义,容易看出如果已经生成了k-1 个元素的排列,那么,k 个元素的排列可以在每个k-1 个元素的排列Pi 前添加元素i 而生成。例如2 个元素的排列是1 2 和2 1,对与个元素而言,

p1 是2 3 和3 2,在每个排列前加上1 即生成1 2 3 和1 3 2 两个新排列,p2和p3则是1 3、3 1和1 2、2 1,按同样方法可生成新排列 2 1 3、2 3 1和3 1 2、3

2 1 。

排 列 组 合 公 式 及 排 列 组 合 算 法

排列组合n选m,组合算法——0-1转换算法(巧妙算法)C++实现 知识储备 排列的定义:从n个不同元素中,任取m(m≤n,m与n均为自然数,下同)个元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素的所有排列的个数,叫做从n个不同元素中取出m个元素的排列数,用符号 A(n,m)表示计算公式: 注意:m中取n个数,按照一定顺序排列出来,排列是有顺序的,就算已经出现过一次的几个数。只要顺序不同,就能得出一个排列的组合,例如1,2,3和1,3,2是两个组合。 组合的定义:从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。用符号 C(n,m) 表示。 计算公式: 注意:m中取n个数,将他们组合在一起,并且顺序不用管,1,2,3和1,3,2其实是一个组合。只要组合里面数不同即可 组合算法 本算法的思路是开两个数组,一个index[n]数组,其下标0~n-1表示1到n个数,1代表的数被选中,为0则没选中。value[n]数组表示组合

的数值,作为输出之用。 ? 首先初始化,将index数组前m个元素置1,表示第一个组合为前m 个数,后面的置为0。? 然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为?“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。一起得到下一个组合(是一起得出,是一起得出,是一起得出)重复1、2步骤,当第一个“1”移动到数组的n-m的位置,即m个“1”全部移动到最右端时;即直到无法找到”10”组合,就得到了最后一个组合。 组合的个数为: 例如求5中选3的组合: 1 1 1 0 0 --1,2,3? 1 1 0 1 0 --1,2,4? 1 0 1 1 0 --1,3,4? 0 1 1 1 0 --2,3,4? 1 1 0 0 1 --1,2,5? 1 0 1 0 1 --1,3,5? 0 1 1 0 1 --2,3,5? 1 0 0 1 1 --1,4,5? 0 1 0 1 1 --2,4,5? 0 0 1 1 1 --3,4,5 代码如下:

算法分析与设计总结

第一章算法概述 1.算法:解决问题的一种方法或过程;由若干条指令组成的有穷指令。 2.算法的性质: 1)输入:有零个或多个输入 2)输出:有至少一个输出 3)确定性:每条指令是清晰的、无歧义的 4)有限性:每条指令的执行次数和时间都是有限的 3.算法与程序的区别 程序是算法用某种程序设计语言的具体实现 程序可以不满足算法的有限性 4.算法复杂性分析 1)算法的复杂性是算法运行所需要的计算机资源的量,需要时间资源的量称为时间复 杂性,需要空间资源的量称为空间复杂性 2)三种时间复杂性:最坏情况、最好情况、平均情况 3)可操作性最好且最有实际价值的是最坏情况下的时间复杂性 第二章递归与分支策略 1.递归概念:直接或间接调用自身的算法 2.递归函数:用函数自身给出定义的函数 3.递归要素:边界条件、递归方程 4.递归的应用 ?汉诺塔问题 void Hanuo(int n,int a,int b,int c) { if(n==1) return; Hanuo(n-1,a,c,b); move(a,b) Hanuo(n-1,c,b,a); } ?全排列问题 void Perm(Type list[],int k,int m) { //产生list[k,m]的所有排列 if(k == m) { for(int i = 0;I <= m;i++) cout<

排 列 组 合 公 式 及 排 列 组 合 算 法 ( 2 0 2 0 )

字符串的排列组合算法合集 全排列在笔试面试中很热门,因为它难度适中,既可以考察递归实现,又能进一步考察非递归的实现,便于区分出考生的水平。所以在百度和迅雷的校园招聘以及程序员和软件设计师的考试中都考到了,因此本文对全排列作下总结帮助大家更好的学习和理解。对本文有任何补充之处,欢迎大家指出。 首先来看看题目是如何要求的(百度迅雷校招笔试题)。一、字符串的排列 用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列,如 abc 的全排列: abc, acb, bca, dac, cab, cba 一、全排列的递归实现 为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑213和321这二个数是如何得出的。显然这二个都是123中的1与后面两数交换得到的。然后可以将123的第二个数和每三个数交换得到132。同理可以根据213和321来得231和312。因此可以知道——全排列就是从第一个数字起每个数分别与它后面的数字交换。找到这个规律后,递归的代码就很容易写出来了: view plaincopy #includeiostream?using?namespace?std;?#includeassert.h?v oid?Permutation(char*?pStr,?char*?pBegin)?{?assert(pStr?pBe

gin);?if(*pBegin?==?'0')?printf("%s",pStr);?else?{?for(char *?pCh?=?pBegin;?*pCh?!=?'0';?pCh++)?{?swap(*pBegin,*pCh);?P ermutation(pStr,?pBegin+1);?swap(*pBegin,*pCh);?}?}?}?int?m ain(void)?{?char?str[]?=?"abc";?Permutation(str,str);?retur n?0;?}? 另外一种写法: view plaincopy --k表示当前选取到第几个数,m表示共有多少个数?void?Permutation(char*?pStr,int?k,int?m)?{?assert(pStr); ?if(k?==?m)?{?static?int?num?=?1;?--局部静态变量,用来统计全排列的个数?printf("第%d个排列t%s",num++,pStr);?}?else?{?for(int?i?=?k;?i?=?m;?i++)?{?swa p(*(pStr+k),*(pStr+i));?Permutation(pStr,?k?+?1?,?m);?swap( *(pStr+k),*(pStr+i));?}?}?}?int?main(void)?{?char?str[]?=?" abc";?Permutation(str?,?0?,?strlen(str)-1);?return?0;?}? 如果字符串中有重复字符的话,上面的那个方法肯定不会符合要求的,因此现在要想办法来去掉重复的数列。二、去掉重复的全排列的递归实现 由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数

全排列生成算法

全排列的生成算法对于给左的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。字典序法按照字典序求下一个排列的算法广例字符集{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

补充全排列算法C语言实现

字符串全排列算法C语言实现 问题描述: 输入一个字符串,打印出该字符串中字符的所有排列。 输入: 123 输出: 123 132 213 231 312 321 问题分析: 现象分析: 这种问题,从直观感觉就是用递归方法来实现即:把复杂问题逐渐简单化,进而得出具体代码实现。(如何发现一个问题可以使用递归方式来解决?经分析可以发现:M 个数的排列方法与N(N>M)个数的排列方法没有区别,处理方法与数据的个数没有关系。 处理方法的一致性,就可以采用递归)。 3个数(123)排列,第一位1不动,剩下两个数(23)的排列,只要相互颠倒一下,就可以出现关于1开头的所有排列 123 132 把2换到第一位,保持不动,剩下的两个数(13)的排列,只要相互颠倒一下,就可以出现关于2开头的所有排列 213 231 同理,把3换到第一位,可得到 312 321 扩展: 把3个数的所有排列,前面加一个4,就可以得到关于4开头的所有的排列4123 4132 4213 4231 4312 4321 若把4与后续数据中的任意一个数据交换,通过完成对后续三个数的全排列,就可以得到相应的数开头的四数的排列。 总结: 对4个数的排列,可以转换成首位不动,完成对3个数的排列 对3个数的排列,可以转换成首位不动,完成对2个数的排列 对2个数的排列,可以转换成首位不动,完成对1个数的排列 对于1个数,无排列,直接输出结果 算法实现说明:

对n个数的排列,分成两步: (1)首位不动,完成对n-1个数的排列, (2)循环将后续其他数换到首位,再次进行n-1个数的排列 注:排列完成后,最终的串要与原串相同 C语言代码实现: #include #include //将串左移一位,首位存到末尾。 void shift( char *s ) { if ( !s||!s[0] ) return ; //security code . null string char ch=s[0]; int i=0; while( s[++i] ) s[i-1]=s[i] ; s[i-1]=ch; } //本函数对于一个已排序好的数据进行全排列 void permutation(char list[], int head ) { int i,len; len=strlen(list) ; if ( len-head == 1 ) //后续没有再排列的,则输出排列数据 { printf( "%s\n", list ); } else { for (i = k; i

算法设计与分析

算法设计与分析期末综合实验试题清单 分治与递归 1-1 合并排序 问题描述:给定n 个整数,利用合并排序思想将其调整成单调序列。 输入:整数的个数n ,以及n 个整数 输出:从大到小排序的n 个整数(或从小到大排序) 1-2 split 快速排序(枢点法) 问题描述:给定n 个整数,利用枢点法快速排序的思想将其调整成单调序列。 输入:整数的个数n ,以及n 个整数 输出:从大到小排序的n 个整数(或从小到大排序) 1-3 平面最近点对 问题描述:平面内有若干点,设计一算法在O (nlogn )时间内求出直线距离最近的一对点,并输它们的距离。 输入:点对的数目n 以及n 对点的坐标 输出:最近的点对坐标(x,y )以及距离d 1-4 棋盘覆盖问题 问题描述:在一个2k ×2k 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该 方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L 型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L 型骨牌不得重叠覆盖。 输入:棋盘的行列数n ,棋盘中特殊方格的行列号(x,y ) 输出:棋盘的覆盖方案 1-5 求k 大(小)元素(基于split 枢点法划分) 问题描述:有一数列,设计一分治递归算法,以Ω(nlogn)时间找出其第k 大(小)元素。 输入:整数的个数n 、n 个整数以及k 值 输出:第k 大(小)元素值以及其对应的下标i 1-6 二分检索 问题描述:有一单调序列,设计一分治算法检索出元素x 。

输入:整数的个数n、n个单调增(减)个整数以及需检索的元素值x 输出:最近的点对坐标(x,y)以及距离d 1-7 大整数乘法(10进制) 问题描述:有两个10制的大整数(不少于30位),设计一分治算法,以O(nlogn)时间算出其乘积。 输入:第一个大整数的位数m、第二个大整数的位数n,以及两个大整数x,y 输出:两个大整数的乘积s 1-8 循环赛比赛安排 问题描述:设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次; (2)每个选手一天只能赛一次; (3)循环赛一共进行n-1天。 输入:选手的个数n、n个选手的编号 输出:每天的赛事安排(共n-1天) 1-9 整数的划分问题 问题描述:将正整数n表示成一系列正整数之和:n=n1+n2+…+n k,其中n1≥n2≥…≥n k≥1,k≥1。正整数n的这种表示称为正整数n的划分。求正整数n的不同划分个数以及划分情况。 输入:正整数n 输出:n的划分个数以及划分情况 1-10 主元素问题 问题描述:有一个整数数列,数列中元素出现次数超过一半的元素定义为主元素,设计一分治算法,求出主元素。 输入:整数的个数n以及n个整数 输出:如果有主元素,输出主元素以及它们所在的位置;如果没有主元素,输出-1 1-11 全排列的生成 问题描述:给出一个序列,生成这个序列的全排列 输入:整数的个数n以及n个整数 输出:生成这n个数的全排列 贪婪算法 2-1 加油站问题 问题描述:一辆汽车加满油后,可行使n千米。旅途中有若干个加油站。若要使沿途加油次数最少,设计一个有效算法,对于给定的n和k个加油站位置,指出应在哪些加油站停靠加油才能使加油次数最少。 输入:汽车加满油后可行驶千米数n,加油站个数k。以及两两加油站之间的距离。 输出:最少的加油次数m,如果无法到达目的地,则输出“No Solution”。

全排列问题的解析

1.5全排列的生成算法 全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。 这里介绍全排列算法四种: (A)字典序法 (B)递增进位制数法 (C)递减进位制数法 (D)邻位对换法 1.5.1字典序法 对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。 [例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列 是:123,132,213,231,312,321。 [注意] 一个全排列可看做一个字符串,字符串可有前缀、后缀。 1)生成给定全排列的下一个排列所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。[例]839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的是987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。 / 1.5.2递增进位制数法 1)由排列求中介数 在字典序法中,中介数的各位是由排列数的位决定的.中介数位的下标与排列的位的下标一致。 在递增进位制数法中,中介数的各位是由排列中的数字决定的。即中介数中各位的下标与排列中的数字(2—n)一致。可看出n-1位的进位链。右端位逢2进1,右起第2位逢3进1,…,右起第i位逢i+1进1,i=1,2,…,n-1. 这样的中介数我们称为递增进位制数。上面是由中介数求排列。 由序号(十进制数)求中介数(递增进位制数)如下: m=m1,0≤m≤n!-1 m1=2m2+kn-1,0≤kn-1≤1 m2=3m3+kn-2,0≤kn-2≤2 …………… mn-2=(n-1)mn-1+k2,0≤k2≤n-2 mn-1=k1,0≤k1≤n-1 p1p2…pn←→(k1k2…kn-1)↑←→m 在字典序法中由中介数求排列比较麻烦,我们可以通过另外定义递增进位制数加以改进。为方便起见,令ai+1=kn-1,i=1,2,…,n-1 (k1k2…kn-1)↑=(anan-1…a2)↑ ai:i的右边比i小的数字的个数 在这样的定义下, 有839647521←→(67342221)↑

全排列生成算法

全排列的生成算法对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。字典序法按照字典序求下一个排列的算法 /*例字符集{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

全排列生成数字或者字母

第一次看到这个算法是在软件设计师的辅导书上。代码如下,在VC++ 7.0下调试通过。// Permutation.cpp : 定义控制台应用程序的入口点。 // //N个数全排列的非递归算法 #include “stdafx.h” void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; } /* 根据当前的排列p,计算下一个排列。 原则是从1234–>4321,若p已经是最后一个排列,传回false,否则传回true。 p是一个n维向量。 */ bool nextPermutation(int *p, int n) { int last = n –1; int i, j, k; //从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。 i = last; while (i > 0 && p[i] < p[i - 1]) i–; //若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回false。 if (i == 0) return false; //从后查到i,查找大于p[i - 1]的最小的数,记入k k = i; for (j = last; j >= i; j–) if (p[j] > p[i - 1] && p[j] < p[k]) k = j; //交换p[k]和p[i - 1] swap(p[k], p[i - 1]); //倒置p[last]到p[i] for (j = last, k = i; j > k; j–, k++) swap(p[j], p[k]);

排 列 组 合 公 式 及 排 列 组 合 算 法

排列组合——排列公式的推理和组合 加法原理和乘法原理,是排列组合中的二条基本原理,在解决计数问题中经常运用。掌握这两条原理,并能正确区分他们,至关重要。 加法原理 若完成一件事情有3类方式,其中第一类方式有1种方法,第二类方式有3种方法,第三类有2种方法,这些方法都不相同,但任选一种方法都可以完成此事,则完成这件事情共有1+3+2=6种方法,这一原理称为加法原理。例如:从甲地到乙地有三类方式,一是汽车,二是火车,三是飞机。若一天中汽车有2班,火车有4班,飞机有一班,那么从甲地到乙地共有多少种不同的走法。共有2+4+1=7种。 乘法原理 若完成一件事情分r个步骤,其中第一个步骤有m1种方法,第二个步骤有m2种方法……第步骤共有mr种方法,各步骤连续或同时完成,这件事才算完成,则完成这件事共有m1*m2*……*mr种方法。例如:从甲地到丙地必须经过乙地。从甲地到乙地有4条路线,从乙地到丙地有3条路线,问从甲地到丙地共有多少种不同的走法?解:要从甲地到达丙地,必须经过两个步骤:先从甲地到乙地,有4条路线;再从乙地到丙地,有3条路线。只有这两个步骤都完成了,才能完成这种事情,缺少哪一个步骤都不行。因此从甲地到丙地共有4*3=12种走法。 加法原理和乘法原理的区别

以上两个基本原理在排列组合问题中将会反复使用。这两个原理回答的都是关于完成一件事情的不同方法的种数问题,但是又有根本区别。加法原理针对的是“分类”问题,若完成一件事情有多类方式,每一类方式的各种方法相互独立,用其中任何一种方法都可以完成这件事情,则用加法原理;而乘法原理针对的是“分步”问题,若完成一件事情必须依次经过多个步骤,每一个步骤的各种方法相互依存,只有各种步骤都完成才算做完成这种事情,则这时用乘法原理。 排列数公式推理过程 例:用1、2、3这3个数字可以组成多少个数字十位和个位不重复的两位数?解:要组成数字不重复的两位数,需要经过两个步骤:第一步确定十位上的数,数字1、2、3都可以放在十位上,共有3种方法;第二步确定个位上的数,因为要求个位数与十位数不能重复,所以个位上的数,只能从三个数字中去掉十位数后所剩的两个数字中任选一个,共有2种方法。只有十位和个位上的数都确定了,才能组成数字不重复的两位数,这两个步骤缺少哪一个都不行。因此,根据乘法原理,3*2=6. 上例中,我们把数字1、2、3称为元素。组成数字不重复的两位数这个问题,从3个不同的元素中任取2个,然后按顺序排成一列数,由于这样的排列与数字不重复的两位数是一一对应的,因此求数字不重复的两位数的个数等同于求这样的排列个数。 推理过程:从n个不同元素中取出m个不同元素排成一列,必须经过m 个步骤。第一步,确定第1个位置上的元素,可以从这n个元素中任取1个放在这个位置上,共有n种方法,即n-(1-1)括号内为位置数减1;第

全排列的生成算法

精品文档 全排列的生成算法全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。任何n个字符集的排列都可以与1?n的n个数字的排列一一对应,因此 在此就以n 个数字的排列为例说明排列的生成法。 n 个字符的全体排列之间存在一个确定的线性顺序关系。所有的排列中除最后一个排列外,都有一个后继;除第一个排列外,都有一个前驱。每个排列的后继都可以从它的 前驱经过最少的变化而得到,全排列的生成算法就是从第一个排列开始逐个生成所有的排列的方法。 全排列的生成法通常有以下几种:字典序法递增进位数制法递减进位数制法邻位交换法 递归类算法 1. 字典序法 字典序法中,对于数字1、2、3 ......n 的排列,不同排列的先后关系是从左到右逐 个比较对应的数字的先后来决定的。例如对于5个数字的排列 1 2354和12345,排列12345 在前,排列12354 在后。按照这样的规定, 5 个数字的所有的排列中最前面的是 1 2345 ,最后面的是54321 。 字典序算法如下: 设P是1?n的一个全排列: p=p1p2 .... pn=p1p2 ...... pj-1pjpj+1 ..... p k-1pkpk+1 .... pn 1 )从排列的右端开始,找出第一个比右边数字小的数字的序号j(j 从左端开始计算),即j=max{i|pipj} (右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者) 3)对换pi ,pk 4 )再将pj+1 ................. pk-1pkpk+1pn 倒转得到排列p ' ' =p1p2 .... p j-1pjpn ..... pk+1pkpk-1 .... p j+1 ,这就是排列p 的下一个下一个排列。 例如839647521 是数字1?9 的一个排列。从它生成下一个排列的步骤如下:自右至左找出排列中第一个比右边数字小的数字 4 839647521 在该数字后的数字中找出比 4 大的数中最小的一个 5 839647521 将5与4交换839657421 将7421 倒转839651247 所以839647521 的下一个排列是839651 247 。 2. 递增进位数制法 在递增进位制数法中,从一个排列求另一个排列需要用到中介数。如果用ki 表示排 列p1p2...pi...pn 中元素pi 的右边比pi 小的数的个数,则排列的中介数就是对应的排 列k1 .... ki ...... kn-1 。 例如排列839647521 的中介数是72642321 ,7、2、6、..... 分别是排列中数字8、3、 1欢迎。下载

c语言递归算法实现数列全排列

1、数列全排列递归算法; 2、在不打印所有全排列时,数列长度分别为10、11、12、13时全排列花费时间测试,修改N的值重新编译即可运行测试; 3、如果需要打印全排列,打开perm函数中的注释掉的两行printf语句即可。

#include #define N 10 int a[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; void swap(int one, int two) { int tmp = a[one]; a[one] = a[two]; a[two] = tmp; } void perm(int *list, int begin, int end)

{ int i = 0; if (begin == end){ for ( i = 0; i < N ; i++){ //printf("%d", list[i]); } //printf("\n"); } else{ for (i = begin; i <= end; i++) { swap(begin,i); perm(list,begin+1,end); swap(begin,i); } } } int main(int argc, char *argv[]) { int *list=a; int i; perm(list, 0, N-1); for (i = 0; i < N; i++){ printf("%d", list[i]); } return 0; }

算法分析与设计常见算法思想

。 算法导论复习——常见算法思想 PPT2-1: 1.堆排序(选择类排序,不稳定) (1)初始化操作:将R[1..n]构造为初始堆; (2)每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。 时间复杂度是:O(nlgn) 2.归并排序 ) 归并排序采用分治法思想的稳定的排序。算法思想是先使每个子序列有序,再使子序列段间有序。 第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置 第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 重复步骤3直到某一指针超出序列尾,将另一序列剩下的所有元素直接复制到合并序列尾。 时间复杂度是:O(nlgn) 3.快速排序(交换类排序,不稳定) | (1)通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据 都比另外一部分的所有数据都要小; (2)然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 时间复杂度是:O(nlgn)。 4.分治法实现大数相乘 将a,b写成前一半数字和后一半数字相加的形式,例如若 a = 5423678,那么a1 = 542,a0 = 3678(注意若不是偶数截取较小一半) 这样a和b相乘就可以写为:a * b = { a1 * 10^(n1/2) + a0 } * { b1 * 10^(n 2/2) + b0 } ( 展开后整理得: a * b = a1*b1 * 10^[ (n1+n2)/2 ] + a1*b0 * 10^(n1/2 ) + a0*b1 * 10^(n2/2) + a0*b0 四项 这样就很容易递归的来求a * b,如果你嫌分解后的数还太大,就可以继续分解。(你可以自己规定在何时结束递归) 时间复杂度是:O(nlgn)

全排列算法解析(完整版)

全排列以及相关算法 在程序设计过程中,我们往往要对一个序列进行全排列或者对每一个排列进行分析。全排列算法便是用于产生全排列或者逐个构造全排列的方法。当然,全排列算法不仅仅止于全排列,对于普通的排列,或者组合的问题,也可以解决。本文主要通过对全排列以及相关算法的介绍和讲解、分析,让读者更好地了解这一方面的知识,主要涉及到的语言是C和C++。本文的节数: 1.全排列的定义和公式: 2.时间复杂度: 3.列出全排列的初始思想: 4.从第m个元素到第n个元素的全排列的算法: 5.全排列算法: 6.全排列的字典序: 7.求下一个字典序排列算法: 8.C++ STL库中的next_permutation()函数:(#include) 9.字典序的中介数,由中介数求序号: 10.由中介数求排列: 11.递增进位制数法: 12.递减进位制数法: 13.邻位对换法: 14.邻位对换法全排列: 15.邻位对换法的下一个排列: 16.邻位对换法的中介数: 17.组合数的字典序与生成: 由于本文的,内容比较多,所以希望读者根据自己的要求阅读,不要一次性读完,有些章节可以分开读。第1节到第5节提供了全排列的概念和一个初始的算法。第6节到第8节主要讲述了字典序的全排列算法。第9到第10节讲了有关字典序中中介数的概念。第11到第12节主要介绍了不同的中介数方法,仅供扩展用。第13节到15节介绍了邻位对换法的全排的有关知识。16节讲了有关邻位对换法的中介数,仅供参考。第17节讲了组合数生成的算法。 1.全排列的定义和公式: 从n个数中选取m(m<=n)个数按照一定的顺序进行排成一个列,叫作从n个元素中取m 个元素的一个排列。由排列的定义,显然不同的顺序是一个不同的排列。从n个元素中取m 个元素的所有排列的个数,称为排列数。从n个元素取出n个元素的一个排列,称为一个全排列。全排列的排列数公式为n!,通过乘法原理可以得到。 2.时间复杂度: n个数(字符、对象)的全排列一共有n!种,所以全排列算法至少时O(n!)的。如果要对全排列进行输出,那么输出的时间要O(n*n!),因为每一个排列都有n个数据。所以实际上,全排列算法对大型的数据是无法处理的,而一般情况下也不会要求我们去遍历一个大型数据的全排列。 3.列出全排列的初始思想: 解决一个算法问题,我比较习惯于从基本的想法做起,我们先回顾一下我们自己是如何写一组数的全排列的:1,3,5,9(为了方便,下面我都用数进行全排列而不是字符)。

算法实验报告1 全排列和会场安排问题

实验一全排列和会场安排问题 一、实验要求 1.全排列:要求键盘输入若干个不同的字符,然后产生并显示所有的排列 会场安排问题: 1.要求按贪心法求解问题; 2.要求读文本文件输入活动安排时间区间数据(数据在文件中的格式如下); 3.不能改变活动的编号,排序时不能移动活动的所有信息; 4.按选择顺序显示活动的编号、开始时间和结束时间。 输入数据的文本文件格式:第1行为活动个数,第2行开始每行一个活动,每个活动的数据有活动编号、开始时间、结束时间,用空格隔开。 如:data.txt 18 1 6 9 2 2 7 ┋有18行 ┋ 2.18 3 5 二、实验平台 1、编程环境:Microsoft Visual Studio 2010 2、运行环境:windows7 三、核心代码 全排列: #include #include"iostream" using namespace std; void Permutation(char* letter, int* record, int length, int n) { if ( n == length ) { static int count = 0; count++; cout << count << "->:\t "; for ( int i = 0; i < length; i++ ) { cout << letter[record[i]]; } cout << endl; return; }

for ( int i = 0; i < length; i++ ) { //确ā?定¨当獭?前°结á点?的?字?符?是?什?么′,?不?能ü和í已?经-确ā?定¨的?字?符?相à同? int j; for ( j = 0; j < n; j++ ) { if ( i==record[j] ) break; } if ( j != n ) continue; //确ā?定¨当獭?前°字?符? if ( j == n ) record[n] = i; Permutation(letter,record,length,n+1); } } int _tmain(int argc, _TCHAR* argv[]) { char* letter = "abcd"; int length = strlen(letter); int* record = new int[length]; Permutation(letter,record,length,0); system("pause"); return 0; } 会场安排问题(贪心算法): #include #include using namespace std; #define N 50 #define TURE 1 #define FALSE 0 int Partition(int *b,int *a,int p,int r); void QuickSort(int *b,int *a,int p,int r); int A[N]; void GreedySelector(int n,int start[],int end[],int *A) { A[1]=true; int j=1;

排 列 组 合 公 式 及 排 列 组 合 算 法

排列组合算法总结(基于C++实现) 全排列n! 1.1 递归法 设一组数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。 如:求{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 5 3、 5 3 4、 5 4 3 六组数。 即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合. #include iostream using namespace std; void Perm(int start, int end, int a[]) { --得到全排列的一种情况,输出结果 if (start == end) { for (int i = 0; i end; i++) cout a[i] ' ';

cout endl; for (int i = start; i end; i++) { swap(a[start], a[i]); --交换 Perm(start + 1, end, a); --分解为子问题a[start+1.,end-1]的全排列 swap(a[i], a[start]); --回溯 int main() { int i, n, a[10]; while (cin n, n) { for (i = 0; i n; i++) a[i] = i + 1; Perm(0, n, a); return 0; C(n,k),n个数中任取k个数 2.1 递归法 实际上就是在n个数中,标记k个数,然后输出这k个数的过程。使用一个visited数组来记录相应下标的数是否被选中。 #include iostream using namespace std; void dfs(int pos, int cnt, int n, int k, int a[],bool visited[]) { --已标记了k个数,输出结果

组合数学中的全排列生成算法完整版

组合数学全排列生成算法 组合数学中的全排列深成算法历来是组合数学考试的重要考察点,因此在这里我简单的介绍一下6种全排列生成算法的详细过程,并借此比较它们之间的优劣之处。 不论是哪种全排列生成算法,都遵循着“原排列”→“原中介数”→“新中介数”→“新排列”的过程。其中中介数依据算法的不同会的到递增进位制数和递减进位制数。关于排列和中介数的一一对应性的证明我们不做讨论,这里仅仅给出了排列和中介数的详细映射方法。相信熟练掌握了方法就可以顺利通过这部分的考察。 递增进位制和递减进位制数 所谓递增进位制和递减进位制数字是指数字的进制随着数字位置的不同递增或递减。通常我们见到的都是固定进制数字,如2进制,10进制等。m位n进制数可以表示的数字是m*n个。而m位递增或递减进位制数则可以表示数字m!个。例如递增进位制数4121,它的进制从右向左依次是2、3、4、5。即其最高位(就是数字4那位)最大值可能是4;第三高位最大可能是3;第二高位最大可能是2;最末位最大可能是1。如果将4121加上1的话,会使最末位得到0,同时进位;第二位的2与进位相加,也会得到0,同时进位;第三位的1与进位相加得到2,不再进位。最终得到结果是4200。递减进位制的道理是一样的,只不过进制从右向左依次是9、8、7、6……,正好与递增进位制相反。很明显,递减进位制的一个最大的好处就是加法不易进位,因为它在进行加法最频繁的末几位里(最右边)进制比较大。 接下来要了解的是递增进位制、递减进位制数和其序号的关系。递增、递减进位制数可以被看作一个有序的数字集合。如果规定递增进位制和递减进位制数的0的序号是十进制0,递增进位制数的987654321和递减进位制数的123456789对应十进制序号362880(即9!),则可以整理一套对应法则。其中,递增进位制数(a1 a2 a3 a4 a5 a6 a7 a8 a9)为: a1*9! + a2*8! + ….+ a8*2! + a9*1! =序号 例如序号100的递增进位制数就是4020,即4*4!+ 0*3!+ 2*2!+ 0*1!=100。将一个序号转换成其递增进位制数首先需要找到一个比序号小的最大阶乘数(即1、2、6、24、120、720……),对其进行整数除得到递增进位制的第一位;将除法的余数反复应用这个方法(当然,之后选择的余数是小一级的阶乘数),直到余数为0。 递减进位制数(a1 a2 a3 a4 a5 a6 a7 a8 a9)为: (((((((((a1 * 1 + a2) * 2 + a3) * 3 + …… + a7) * 8 + a8) * 9 + a9=序号 例如序号100的递减进位制数就是131(a7 a8 a9,即从后对齐),即(1*8 + 3)*9 + 1 = 100。将一个序号转换成其递减进位制数,需要对序号用9取余数,就可以得到递减进位制的最末位(这点和递增进位制先算出最高位相反)。用余下的数的整数除结果重复此过程(当然,依次对8、7、6……取余),直到余数为0。 关于递增进位制和递减进位制需要注意的重点:一是其加减法的进位需要小心;二是序号和数字的转换。除了100之外,常见的转换有:999的递增数是121211,递减数是1670;99的递

相关主题