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.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

戴方晴画的图:

#include <iostream>
#include <algorithm>
#include <vector>
#include <assert.h>

using namespace std;

class Solution {
public:
    /**
    Think about the start string is 12345.
    If we want to increase the digit “1″ to “2″, like from “12345″ to “21345″, how many permutations in between? 4! = 4*3*2*1.
    If we want to increase the permutation from “21345″ to “23145″, how many permutations in between? 3!
    So, we can calculate (n-1)!, (n-2)!,  …. 2!, 1! , and then use them to identify each digit. 
    If k> 3*(n-1)! and k<4*(n-1)!, then the first digit should be 4.
    
    n: 4 i: 1 result: 1234  
    n: 4 i: 2 result: 1243
    n: 4 i: 3 result: 1324   look at: 1^7  2^8 
    n: 4 i: 4 result: 1342
    n: 4 i: 5 result: 1423
    n: 4 i: 6 result: 1432
    n: 4 i: 7 result: 2134  2到前面 3! = 6  1+6 = 7  最小的元素交换
    n: 4 i: 8 result: 2143      4*3*2 = 24个大小  
    n: 4 i: 9 result: 2314
    n: 4 i: 10 result: 2341
    n: 4 i: 11 result: 2413
    n: 4 i: 12 result: 2431
    n: 4 i: 13 result: 3124
    n: 4 i: 14 result: 3142
    n: 4 i: 15 result: 3214
    n: 4 i: 16 result: 3241
    n: 4 i: 17 result: 3412
    n: 4 i: 18 result: 3421
    n: 4 i: 19 result: 4123
    n: 4 i: 20 result: 4132
    n: 4 i: 21 result: 4213
    n: 4 i: 22 result: 4231
    n: 4 i: 23 result: 4312
    
    */
    string nextKPermutation(int n, int k) {
        assert(n>=0 && k>0 && n<=9);
        vector <int> m(n, 1);
        int tmp = 1;
        for (int i=0; i<n-1; ++i) {
            m[n-i-2] = tmp;
            tmp *= i+2;
        }
        k = (k-1)%(m[0]*n);
        vector <bool> used(n, false);
        string ans;
        for (int i=0; i<n; ++i) {
            int idx = k/m[i];
            k = k%m[i];
            for (int j=0; j<n; j++)
                if (!used[j]) {
                    if (!idx) {
                        ans = ans + char(j+'1');
                        used[j] =true;
                        break;
                    }
                    idx--;
                }
        }
        return ans;
    }
    
    void nextPermutation(vector<int> &num) {
        vector<int>::iterator i, ii, j;
        if (num.size() == 0 || num.size() == 1){
            return;
        }
        /**
                                                                           
                                                                           
            1) 从后往前找到第一个相邻的pair,i和ii,使得 *i < *ii:于是可以确定 包括 [ii,last) 都是逆序的。 
                        [First,i) ---We don't care
                        [i,ii)    ---first *i < *ii
                        [ii,Last) ---全部都是逆序的,前面的大后面的小,所以反过来整体也不会变大
                        
                        这个时候iter_swap(i,ii)显然会让变大,可是有风险,第一ii有可能过大,第二交换过来之后i之后的需要变成最小才好。

            2) 然后再从尾巴上找第一个 *i < *j,第一个j。 那么就有 [ii,*j, last)是逆序的,显然[ii,*i,last) 还是逆序的。
               why? 因为 [j+1,last) < *i < *j < [ii,*j)
            3) iter_swap(i,j); //注意这里不是 swap(i,j) 也可以使用swap(*i,*j);
            4) 现在确定是变大了,然后因为[ii,last) 之后,还是逆序的,最后reverse([ii,last))确保反过来就可以
            
            PrevPermutation也是类似的,找第一个相邻对 *i > *ii,然后从后往前找*j < *i,然后同样的办法就ok
            
            如果是找k'th Permutation呢? 重复做k次nextPermutaion肯定OK,是否有更好的办法呢?
            Think it again. If you still think this is a good algorithm, take n=9 and k= 61237. 
            It will take a long time to calculate the kth permutation. Can we do better? Sure!
        
        */
        
        ii = num.end();
        j = num.end();
        i = ii -1;
        for(;i != num.begin();){
            ii--;
            i--;
            if (*i < *ii){
                while(!(*i < *--j));
                std::iter_swap(i,j);
                std::reverse(ii,num.end());
                return;
            }
        }
        //说明是逆序,just reverse
        reverse(num.begin(),num.end());
    }
};

using namespace std;
int main(int argc, char *argv[]) {
    int a[] = {1,3,2};
    vector<int> v(a,a+3);
    Solution sol;
    sol.nextPermutation(v);
    
    int n = 4, k = 1;
    for(int i = 1; i < 4*3*2; i++)
    cout << "n: " << 4 << " i: "<< i << " result: "<< sol.nextKPermutation(n,i) << "\n";

    return 0;
}

 

posted @ 2013-06-14 19:02  一只会思考的猪  阅读(241)  评论(0编辑  收藏  举报