问题描述:
给定一个整数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\t",Array[i]);
}
printf("\n");
while(next_permutation(Array,Array + 5))
{
for(int i = 0; i < 5; ++i)
{
printf("%d\t",Array[i]);
}
printf("\n");
}
return 0;
}