问题描述:
给定一个整数n,按字典序打印出1-n的所有全排列
问题解答:
1.用C++里的next_permutation函数
函数定义如下:
template<class BidirectionalIterator> bool next_permutation( BidirectionalIterator _First, BidirectionalIterator _Last ); template<class BidirectionalIterator, class BinaryPredicate> bool next_permutation( BidirectionalIterator _First, BidirectionalIterator _Last, BinaryPredicate _Comp );
函数实现的原理:
从序列的最后一个元素开始向前扫描,直到找到两个位置上相邻的元素*i,*j(i,j为下标,j = i+1)且*i<*j,再从尾端找一个元素*k,*i<*k,将*i与*k交换,并将*j及之后的元素全部倒置
证明:
设给定序列为a1a2...aiai+1ai+2...an,且ai,ai+1为逆序扫描第一个找到的正序,则有ai < ai+1 > ai+2 > ai+3 > ... > an,
①假设ai > ak (∀k∈{i+2,...n}),那么直接交换ai与ai+1,设所得序列为a1'a2'...ai'ai+1'ai+2' ... an',那么a1'=a1...ai-1'=ai,ai' = ai+1,ai+1'=ai,ai+2'=ai+2,...,an'=an
再将ai+1'及之后的元素逆置一下,就可以得到ai+1'及之后字符的集合组成的串的最小字典序排列
为什么这种是最小的呢?
首先,如果改变ai之前的字符任意使得字典序变得更大,那么显然要比上述方法得到的字符串a1'...an'字典序要大;
又ai+1及之后的串已经是完全逆序,所以只改变这部分得到的字符串字典序不会更大;
②假设存在k∈{i+2..n},使得ai<ak,证明类似
ps:注意边界条件:当字符串已经为完全逆序(达到字典序最大)时,没有next_permutation
函数代码:
template<class BidirectionalIterator> bool next_permutation( BidirectionalIterator first, BidirectionalIterator last ) { if(first == last) return false; //空序列 BidirectionalIterator i = first; ++i; if(i == last) return false; //一个元素,没有下一个序列了 i = last; --i; for(;;) { BidirectionalIterator ii = i; --i; if(*i < *ii) { BidirectionalIterator j = lase; while(!(*i < *--j)); iter_swap(i, j); reverse(ii, last); return true; } if(i == first) { reverse(first, last); //全逆向,即为最小字典序列,如cba变为abc return false; } } }
使用:
#include<iostream> #include<cstdio> #include<string> #include<string.h> #include<cstring> #include<algorithm> using namespace std; int main() { int Array[5] = {1,2,3,4,5}; for(int i = 0; i < 5; ++i) { printf("%d ",Array[i]); } printf(" "); while(next_permutation(Array,Array + 5)) { for(int i = 0; i < 5; ++i) { printf("%d ",Array[i]); } printf(" "); } return 0; }