1.Next Permutation
原文链接:点击这里进入
基本内容:
解题思想:
所谓一个排列的下一个排列的意思就是 这一个排列与下一个排列之间没有其他的排列。这就要求这一个排列与下一个排列有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。可能理解起来比较难懂。
对于数字序列:
先看前面2排的话,可以看出来第二排是比第一排要大的,参考字符串比较大小的问题。那么第2个排列是不是第一个排列的下一个排列呢。很明显不是,第3个排列才是, 那么如何获取到下一个排列呢。步骤比较简单:假设数组大小为 n
1.从后往前,找到第一个 A[i-1] < A[i]的。也就是第一个排列中的 6那个位置,可以看到A[i]到A[n-1]这些都是单调递减序列。
2.从 A[n-1]到A[i]中找到一个比A[i-1]大的值(也就是说在A[n-1]到A[i]的值中找到比A[i-1]大的集合中的最小的一个值)
3.交换 这两个值,并且把A[n-1]到A[i]排序,从小到大。
更好的流程图:来源自: http://fisherlei.blogspot.com/2012/12/leetcode-next-permutation.html
代码:
//Link:http://oj.leetcode.com/problems/next-permutation/
//265 / 265 test cases passed.
//Status: Accepted
//Runtime: 60 ms
void nextPermutation(vector<int> &num) {
int length = num.size() - 1;
while( length > 0 ){
if( num[ length - 1 ] < num[ length ] )
break;
length --;
}
if( length == 0 ){
for( int i = 0, j = num.size(); i < j; i++ ,j-- )
swap( num[ i ], num[ j ] );
}
int k = num.size() - 1;
for( ; k >= length; k-- )
if( num[ k ] > num[ length - 1 ] )
break;
swap( num[ k ], num[ length - 1 ] );
sort( num.begin() + length , num.end() );
}
另外在C++的标准库中有“求一个数字序列的下一个排列”的函数next_permutation,可以实现下一个排列的功能。当然这种函数在A题的时候是可以用的,但是在面试的时候直接调用是不行的。
代码:
void nextPermutation(vector<int> &num) {
next_permutation( num.begin(), num.end() );
}
源代码在此:
/**
* @brief Permute range into the next @e dictionary ordering.
* @ingroup sorting_algorithms
* @param __first Start of range.
* @param __last End of range.
* @return False if wrapped to first permutation, true otherwise.
*
* Treats all permutations of the range as a set of @e dictionary sorted
* sequences. Permutes the current sequence into the next one of this set.
* Returns true if there are more sequences to generate. If the sequence
* is the largest of the set, the smallest is generated and false returned.
*/
template<typename _BidirectionalIterator>
bool
next_permutation(_BidirectionalIterator __first,
_BidirectionalIterator __last)
{
// concept requirements
__glibcxx_function_requires(_BidirectionalIteratorConcept<
_BidirectionalIterator>)
__glibcxx_function_requires(_LessThanComparableConcept<
typename iterator_traits<_BidirectionalIterator>::value_type>)
__glibcxx_requires_valid_range(__first, __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 = __last;
while (!(*__i < *--__j))
{}
std::iter_swap(__i, __j);
std::reverse(__ii, __last);
return true;
}
if (__i == __first)
{
std::reverse(__first, __last);
return false;
}
}
}
全排列:
既然求出了一个排列的下一个排列,那么我们就可以了解下一个序列的全排列是怎么实现的。
1.递归算法
以 数组 [1,2,3]为栗子,如何生成它的全排列呢。在第一个位置上我们有3种选择,分别是1,2,3。当选中第1个位置后,第二个位置上我们有2个选择。当第一个位置选中1的时候,第二个位置我们可以选2,或者3. 当第一个位置选中2的时候,第二个位置我们可以选1,或者3. 当第一个位置选中3的时候,第二个位置我们可以选1,或者2. 同理第3个位置在前2个位置都确定的情况下只有一种选择了。所以它的全排列个数为n!个。如下图所示(图片太烂了)
了解了全排序的生成方式我们可以很明显的看出来这个可以用递归来解(当第一个位置确定为1的时候,需要用到后面2个位置的全排列),So我们可以写出来代码:
代码:
void Swap( int & x, int & y ){
int temp = x;
x = y;
y = temp;
}
void AllPersutation( int A[ ], int position, int length ){
if( position == length - 1 ){
static int num = 1;
cout << "The " << num++ << " Persutation is :";
for( int i = 0; i < length; i++ )
cout << A[ i ] << " ";
cout << endl;
}
else{
for( int i = position; i < length; i++ ){
Swap( A[ position ], A[ i ] );
AllPersutation(A, position + 1, length );
Swap( A[ position ], A[ i ] );
}
}
}
其中最后一个Swap是为了保证你调用了下一步AllPersuatation函数之后得到下一个全排列之后,能够让交换位置的元素还原。以便下一个交换正确完成。