Permutations系列

一. 全排列递归算法

1. 数据没有重复的情况下

算法:每个元素依次与后面的数进行交换

例子:假设元素为123,则递归交换事例如下:


2. 数据有重复的情况下、

算法:在没有数据重复的全排列下,交换需要加上前提条件,即元素应该和后面没有重复出现的数字进行交换,即当访问到第k个元素的时候,如果 [k, j)中,没有元素与num[j]相等,则可以交换num[k],num[j]


二. 全排列的迭代算法

参照STL中的实现,算法如下:

1. 在序列中,从后往前寻找第一对递增的元素对,前面一个数为替换数,位置为替换点

2. 接着依然从后往前寻找第一个大于替换数的元素(一定可以找到),记为A

3. 交换替换数与A,然后反转替换点后面的序列

4. 步骤1中如果没有找到,直接反转整个序列


leetcode 中 Permutation Sequence,

Permutations II

 ,Next Permutation均使用上面的算法,代码如下:

 

 

class Solution {
public:
    //没有重复的情况
    vector<vector<int> > permute(vector<int> &num) {
        ans.clear();
        allrange(num, 0, num.size());
        return ans;
    }
    
    void allrange(vector<int>& num, int k, int n) {
        if( k == n ) ans.push_back(num);        //扫完最后一个元素的时候,num数组就是一个全排列
        for(int i=k; i<n; ++i) {    //从k开始,第k个数依次与后面的数进行交换
            swap(num[k], num[i]);
            allrange(num, k+1, n);  //交换完,开始进行后面部分的全排列
            swap(num[k], num[i]);
        }
    }
    
    //有重复的情况
    vector<vector<int> > permuteUnique(vector<int> &num) {
        ans.clear();
        allrange2(num, 0, num.size());
        return ans;
    }
    
    //在[ibegin,iend)中寻找是否有=num[iend]的元素,有则不交换,没有就交换
    bool isNeedSwap(vector<int>& num, int ibegin, int iend) {
        for(int i=ibegin; i<iend; ++i)
            if( num[i] == num[iend] ) return false;
        return true;
    }
    
    void allrange2(vector<int>& num, int k, int n) {
        if( k == n ) ans.push_back(num);    //扫完最后一个元素,num数组就是一个全排列
        for(int i=k; i<n; ++i)  //从k开始,第k个数依次与后面的数进行交换
            if( isNeedSwap(num, k, i) ) {   //若没有重复,则交换
                swap(num[k], num[i]);
                allrange2(num, k+1, n);     //交换完,开始进行后面部分的全排列
                swap(num[k], num[i]);
            }
    }
    
    //求下一个全排列
    void nextPermutation(vector<int> &num) {
        vector<int>::iterator cur = num.end();
        vector<int>::iterator pre = cur-1;
        while( pre != num.begin() ) {
            --pre;  //指向cur前面一个元素
            --cur;
            if( *pre < *cur ) { //第一对递增的元素,pre为替换点,*pre为替换数
                cur = --num.end();
                while( *pre >= *cur ) --cur;    //从后往前寻找第一个大于*pre的元素,并与*pre交换
                swap( *pre, *cur );
                reverse(pre+1, num.end());  //反转替换点之后的元素
                return ;
            }
        }
        reverse(num.begin(), num.end());    //数列已是“最后”的序列是,则直接反转
        return ;
    }
    
private:
    vector< vector<int> > ans;
};

Permutation Sequence

 

 

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

 

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

 

这题如果使用next_permutation函数,则会超时。这时我们使用另一种算法,直接看下面例子。
假设数列有3个元素 1 2 3,求第4个序列,则有:
序号   排列
1        123
2        132
3        213
4        231
5        312
6        321
3个元素的时候,则1开头,23的全排有2!= 2种,2开头,13全排有2种,3开头,12全排有2种。
由于要求第4个序列,则会发现 2 < 4 <= 4,这时第1位选择2。
此时元素剩下13,k更新为4-2=2,即在13的全排中求第2个序列。那么1开头,3的全排只有1种,3开头,1的全排只有1种,即序号为
序号   排列
1         13
2         31
由于求第2个序列,则会发现 1 < 2 <= 2,这时第2位选择3,最后元素只剩下1了,那么所求即为231
代码如下:
class Solution {
public:
    string getPermutation(int n, int k) {
        string str = string("123456789").substr(0, n);  //取前n个数字
        string ans;
        for(int i=0; i<n; ++i) k = getKth(ans, str, k);     //每次获取第i个数
        return ans;
    }
    
    int getKth(string& ans, string& str, int k) {
       int num = fac(str.size()-1); //求(str.size()-1)!
       int idx = (k-1) / num;   //这位应选取的数是str[idx],注意下标是从0开始
       ans.push_back(str[idx]); //放入ans中
       str.erase(idx, 1);   //原数列删除str[idx]
       return k - idx * num;    //返回新k值
    }
    
    int fac(int n) {    //求n的阶乘
        int ans = 1;
        for(int i=2; i<=n; ++i) ans *= i;
        return ans;
    }
};





 

posted on 2014-08-16 16:05  bug睡的略爽  阅读(171)  评论(0编辑  收藏  举报

导航