全排列算法的非递归实现与递归实现的方法(C++)
(一)非递归全排列算法
基本思想是:
1.找到所有排列中最小的一个排列P.
2.找到刚刚好比P大比其它都小的排列Q,
3.循环执行第二步,直到找到一个最大的排列,算法结束.
下面用数学的方法描述:
给定已知序列 P = A1A2A3An ( Ai!=Aj , (1<=i<=n , 1<=j<=n, i != j ) )
找到P的一个最小排列Pmin = P1P2P3Pn 有 Pi > P(i-1) (1 < i <= n)
从Pmin开始,总是目前得到的最大的排列为输入,得到下一个排列.
方法为:
1.从低位到高位(从后向前),找出“不符合趋势”的数字。即找到一个Pi,使Pi < P(i+1)。
若找不到这样的pi,说明我们已经找到最后一个全排列,可以返回了。
2.在 P(i+1)P(i+2)Pn 中,找到一个Pj,便得 Pj"刚刚好大于"Pi.
("刚刚好大于"的意思是:在 P(i+1)P(i+2)Pn 中所有大于Pi的元素构成的集合中最小的元素.)
3.交换 Pi , Pj 的位置.注意:此处不改变i和j的值,改变的是Pi和Pj.
4.交换后, P1P2P3Pn 并不是准确的后一个排列。因为根据第1步的查找,我们有P(i+1) > P(i+2) > . > Pn
即使进行了Pi和Pj的交换,这仍然是这一部分最大的一个排列。将此排列逆序倒置(变成最小的排列)即为所求的下一个排列.
5.重复步骤1-4,直到步骤1中找不到“不符合趋势”的数字.
//交换数组a中下标为i和j的两个元素的值
void swap(int* a,int i,int j)
a^=a;
a^=a;
a^=a;
//将数组a中的下标i到下标j之间的所有元素逆序倒置
void reverse(int a,int i,int j)
for(;i<j;++i,--j)
swap(a,i,j);
void print(int a,int length)
for(int i=0;i<length;++i)
cout<<a<<" ";
cout<<endl;
//求取全排列,打印结果
void combination(int a,int length)
if(length<2) return;
bool end=false;
while(true)
print(a,length);
int i,j;
//找到不符合趋势的元素的下标i
for(i=length-2;i>=0;--i)
if(a<ai+1) break;
else if(i==0) return;
for(j=length-1;j>i;--j)
if(a>a) break;
swap(a,i,j);
reverse(a,i+1,length-1);
(二)递归算法
令E= e1 , ..., en 表示n 个元素的集合,我们的目标是生成该集合的所有排列方式。令Ei 为E中移去元素i 以后所获得的集合,perm (X) 表示集合X 中元素的排列方式,ei . p e r m(X)表示在perm (X) 中的每个排列方式的前面均加上ei 以后所得到的排列方式。例如,如果E= a, b, c,那么E1= b, c,perm (E1 ) = ( b c, c b),e1 .perm (E1) = (a b c, a c b)。对于递归的基本部分,采用n = 1。当只有一个元素时,只可能产生一种排列方式,所以perm (E) = ( e),其中e 是E 中的唯一元素。当n > 1时,perm (E) = e1 .perm (E1 ) +e2 .p e r m(E2 ) +e3.perm (E3) + ⋯ +en .perm (En )。这种递归定义形式是采用n 个perm (X) 来定义perm (E), 其中每个X 包含n- 1个元素。至此,一个完整的递归定义所需要的基本部分和递归部分都已完成。
当n= 3并且E=(a, b, c)时,按照前面的递归定义可得perm (E) =a.perm ( b, c ) +b.perm ( a,c ) +c.perm ( a, b )。同样,按照递归定义有perm ( b, c ) =b.perm ( ) +c.perm ( ), 所以a.perm ( b, c ) = ab.perm ( ) + ac.perm ( ) = a b . c + ac.b = (a b c, a c b)。同理可得b.perm ( a, c) = ba.perm ( ) + bc.perm ( ) = b a . c + b c . a = (b a c, b c a),c.perm ( a, b) =ca.perm ( ) + cb.perm ( ) = c a . b + c b . a = (c a b, c b a)。所以perm (E) = (a b c, a c b, b a c, b c a,c a b, c b a)。注意a.perm ( b, c )实际上包含两个排列方式:abc 和a c b,a 是它们的前缀,perm ( b, c )是它们的后缀。同样地,ac.perm ( ) 表示前缀为a c、后缀为perm ( ) 的排列方式。程序1 - 1 0把上述perm (E) 的递归定义转变成一个C++ 函数,这段代码输出所有前缀为l i s t 0:k-1, 后缀为l i s t k:m 的排列方式。调用Perm(list, 0, n-1) 将得到list0: n-1 的所有n! 个排列方式,在该调用中,k=0, m= n - 1,因此排列方式的前缀为空,后缀为list0: n-1 产生的所有排列方式。当k =m 时,仅有一个后缀l i s t m ,因此list0: m 即是所要产生的输出。当k<m时,先用list 与l i s t k:m 中的每个元素进行交换,然后产生listk+1: m 的所有排列方式,并用它作为list0: k 的后缀。S w a p是一个inline 函数,它被用来交换两个变量的值
template <class T>
inline void Swap(T& a, T& b)
// 交换a和b
T temp = a; a = b; b = temp;
template<class T>
void Perm(T list, int k, int m)
//生成list k:m 的所有排列方式
int i;
if (k == m)
//输出一个排列方式
for (i = 0; i <= m; i++)
cout << list ;
cout << endl;
else // listk:m 有多个排列方式
// 递归地产生这些排列方式
for (i=k; i <= m; i++)
Swap (list, list);
Perm (list, k+1, m);
Swap (list , list );
您可能感兴趣的文章
- 01-10使用C++实现全排列算法的方法详解
- 01-10深入第K大数问题以及算法概要的详解
- 01-10深入N皇后问题的两个最高效算法的详解
- 01-10用C++实现DBSCAN聚类算法
- 01-10深入全排列算法及其实现方法
- 01-10贪心算法 WOODEN STICKS 实例代码
- 01-10C语言字符串操作总结大全(超详细)
- 01-10异步http listener 完全并发处理惩罚http恳求的小例子
- 01-10深入探讨C语言中局部变量与全局变量在内存中的存放位置
- 01-10深入分析C中不安全的sprintf与strcpy


阅读排行
本栏相关
- 04-02c语言函数调用后清空内存 c语言调用
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言的正则匹配函数 c语言正则表达
- 04-02c语言用函数写分段 用c语言表示分段
- 04-02c语言中对数函数的表达式 c语言中对
- 04-02c语言编写函数冒泡排序 c语言冒泡排
- 04-02c语言没有round函数 round c语言
- 04-02c语言分段函数怎么求 用c语言求分段
- 04-02C语言中怎么打出三角函数 c语言中怎
- 04-02c语言调用函数求fibo C语言调用函数求
随机阅读
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-11ajax实现页面的局部加载
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-10C#中split用法实例总结
- 04-02jquery与jsp,用jquery
- 01-10SublimeText编译C开发环境设置
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-10delphi制作wav文件的方法
- 01-10使用C语言求解扑克牌的顺子及n个骰子