这是一道关于数组排列的题目,题面如下:
事实上,熟悉C++ STL的朋友一看到这道题,应该就联想到这是STL中已经提供的的一个泛型算法。在STL中的算法功能和此题描述的完全一致,只不过在STL中返回值是bool类型,如果存在下一个更大的排列,那么就返回true,并将容器修改为下一个更大的排列。C++ Primer 5th edition中附录解释如下:
所以其实这道题的AC代码就是…
// using STL generic algorithm directly, hahaha...
next_permutation(nums.begin(), nums.end());
亲测,这样真的是可以通过的:)
好了,下面说说这道题的解法,首先我按顺序列出123这三个数字的组合,从上到下为应该返回的顺序:
123
132
213
231
312
321
不得不说这道题还是有些难度的,很难去解释背后的原理或者奥秘。这次就当一个树洞,把算法流程大致描述出来,一方面算是加深了对STL中算法的实现的理解,也以备以后回顾,以下end表示最后一个元素的索引:
1.令i从后向前遍历序列,找到第一个满足nums[i] < nums[i+1]的元素下标i,同时可以推知[i+1,end]都是降序序列。如果i<0,表示当前序列完全降序,已经达到最大字典序,直接转到4,否则执行2,3,4。
2.令j再次从后向前遍历序列,找到第一个满足nums[i] < nums[j]的元素下标j。
3.交换元素nums[i]和nums[j]。
4.将序列的[i+1, end]部分完全逆置。
这个算法的思想就是尽可能保证找到的交换数对(nums[i], nums[j])是数值上最接近的且最靠近结尾的,在交换完成之后通过逆置交换点之后的数据来保证序列的升序,升序是最逼近下一个字典序排列的情况。
完整的代码如下:
class Solution {
/*swap the two elements in vector, i & j are index*/
void swap(vector<int>&nums, int i, int j)
{
if(i == j)
return;
/*intermediate variable*/
int Temp;
Temp = nums[i], nums[i] = nums[j], nums[j] = Temp;
}
/*reverse the whole vector to get the min permutation*/
void reverse(vector<int>& nums, int h, int t)
{
int Head = h, Tail = t;
while(Head <= Tail)
swap(nums, Head++, Tail--);
}
public:
void nextPermutation(vector<int>& nums) {
// using STL generic algorithm directly, hahaha...
// next_permutation(nums.begin(), nums.end());
if(nums.size() == 1)
return;
int Tail = nums.size() - 2, SearchBigger = nums.size() - 1;
/*find the element's index Tail which satisfies nums[i] < nums[i + 1]*/
/*Tail is as big as possible*/
while(Tail >= 0 && nums[Tail] >= nums[Tail + 1] )
--Tail;
/*if the element can be found, then search the closest bigger element*/
if(Tail >= 0)
{
while(SearchBigger > Tail && nums[SearchBigger] <= nums[Tail])
--SearchBigger;
swap(nums, SearchBigger, Tail);
}
/*reverse the array[Tail+1, End] to get ready for next permutation*/
reverse(nums, Tail + 1, nums.size() - 1);
}
};
此算法仍需反复斟酌