LeetCode31 Next Permutation

题目:

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place, do not allocate extra memory. 

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column. (Medium)
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

分析:

题意就是找下一个排列,首先可以先复习使用一下STL的next_permutation。

调用next_permutation肯定是能过的。

代码:

1 class Solution {
2 public:
3     void nextPermutation(vector<int>& nums) {
4         next_permutation(nums.begin(), nums.begin() + nums.size());
5     }
6 };

用next_permutation生成全排列举例如下(参考C++ reference)

 1 // next_permutation example
 2 #include <iostream>     // std::cout
 3 #include <algorithm>    // std::next_permutation, std::sort
 4 
 5 int main () {
 6   int myints[] = {1,2,3};
 7 
 8   std::sort (myints,myints+3);
 9 
10   std::cout << "The 3! possible permutations with 3 elements:\n";
11   do {
12     std::cout << myints[0] << ' ' << myints[1] << ' ' << myints[2] << '\n';
13   } while ( std::next_permutation(myints,myints+3) );
14 
15   std::cout << "After loop: " << myints[0] << ' ' << myints[1] << ' ' << myints[2] << '\n';
16 
17   return 0;
18 }

所以一般用到next_permutation的题目就是在上面循环的do里面对每次生成的排列做文章。

回头再来看看实现:

这个算法的实现貌似是一个诞生了几百年的经典算法,但是很不好理解其做法的原因,自己梳理一下。

首先要注意的是,我们动一个元素的时候,应该想要改变之后的增值尽可能小。如 124653 -> 125346;

也就是说,高位应该尽量一致,能动低位的时候就不要动高位。如上例子,当4改成5就能增大的时候,不要动1,2。

那4是如何找到的,也就是说最后一个能动的地位是谁呢? 这就应该从后往前看,显然53没得动,653也没得动,但4653可以动了。

原因在于,如果从后往前看的时候,得到的后方元素都是递减的,也就是在这一局部(比如653)他已经没有next_permutation了,所以要再向前找。

只到发现一个位置i, nums[i] < nums[i+1]这意味着 nums[i....size-1] (如4653)这一局部是还有next_permutation。所以位置 i 就是需要被交换。

但他应该交换谁呢?还是考虑上面说的想要改变之后的增值尽可能小,所以应该交互大于nums[i]的最小值,也就是后面位置中从后往前数(从小往大数)第一个大于nums[i]的元素。

当交换完之后,即例子中变为125643,可以发现。nums[i+1,...size-1](即643)一定是完全降序的。

所以为了能组成元素的最小值(这样增值才最小),应该reverse这一部分,变为(346)

得到最终结果125346。

 

所以综合来讲,算法的流程是(伪代码):

It i = end - 1;
while ( (*i > *(i+1) ))  //找第一个小于后续元素的位置
    /* pass */;

It j = end;
while ( *j < *i  )  //找第一个大于*i的元素    
    /*pass */

iter_swap(i, j);   //交换 *i , *j
reverse(i+1, end); // reverse *(i+1)到*end
return true;

代码:

 1 class Solution {
 2 public:
 3     void nextPermutation(vector<int>& nums) {
 4         if (nums.size() < 2) {
 5             return;
 6         }
 7         int i = nums.size() - 2;
 8         for (i; i >= 0; i--) {
 9             if (nums[i] < nums[i+1]) {
10                 break;
11             }
12         }
13         if (i == -1) {
14             sort(nums.begin(), nums.end());
15             return;
16         }
17         int j = nums.size() - 1;
18         for (j; j > i; j--) {
19             if (nums[j] > nums[i]) {
20                 break;
21             }
22         }
23         swap(nums[i], nums[j]);
24         reverse(nums.begin() + i + 1, nums.end());
25         
26     }
27 };

 

 

 
posted @ 2016-08-19 22:47  wangxiaobao1114  阅读(186)  评论(0编辑  收藏  举报