1. 程式人生 > >排列組合(permutation)系列解題報告

排列組合(permutation)系列解題報告

本文講解4道關於permutation的題目:

1. Permutation:輸出permutation——基礎遞迴 
2. Permutation Sequence: 輸出字典序排列的第k個permutation——推理
3. Next Permutation:給定一個permutation中的序列,求字典序它的下一個permutation是什麼——邏輯推理
4. Permutation II:和第一題有細微的差別: 對於一個可能有重複元素的陣列輸出所有permutation——有條件dfs


——基礎遞迴

 class Solution{
     private:
         vector<vector<int> > L;
         vector<int> Nums;
         int l;
         vector<bool> visited;
     public:
         void perm(vector<int>& list){
             if(list.size()==l){
                 L.push_back(list);
                 return;
             }
             for(int i=0; i<l; i++){
                 if(!visited[i]){
                     list.push_back(Nums[i]);
                     visited[i] = true;
                     perm(list);
                     list.pop_back();
                     visited[i] = false;
                 }
             }
         }
 
 
         vector<vector<int> > permute(vector<int> &num){
             int i;
             Nums = num;
             l = Nums.size();
             for(i=0;i<l;i++)
                 visited.push_back(false);
             vector<int>list;
             perm(list);
 
             //for(i=0;i<L.size();i++){
             //    for(int j = 0;j<l;j++)
             //        cout<<L[i][j];
             //    cout<<endl;
             //}
             return L;
         }
 };


——邏輯推理

醬想,n位的permutation有n!個,那麼第k個permutation如果滿足n!<k<(n+1)!就一定有,

a = k/n!

b = k%n!

取集合裡的第a位做下一位,下一次分析剩下的字元組成的第b個permutation

------------------------

e.g. [1,2,3,4]組合的第10個

①求[1,2,3,4]組合的第10個

10/3! = 1…4 --->找到[1,2,3,4]中第(1+1)個數(2)做下一位,留下[1,3,4]

②求[1,3, 4]組合的第10-3! * 1 = 4個

4/2! = 2…0 --->找到[1,3,4]中第2個數(3)做下一位, 留下[1,4]

③餘零,說明是permutation裡的最後一個 -> 剩下的逆序輸出

--->2341

class Solution {
public:
    string getPermutation(int n, int k) {
        int i,j,sum = 1;
        //sum = (n-1)!
        for (i=2; i<n; i++) {
            sum *= i;
        }
        bool visited[n+2];
        memset(visited, false, sizeof(visited));
        string str;
        for(i=1;i<n;i++){
            int nextidx = k/sum;
            k = k%sum;
            if(k==0)
                nextidx -- ;
            sum/=(n-i);
            int cnt = 0;
            for (j=0; j<n; j++) {
                if (!visited[j]) {
                    if (cnt == nextidx){
                        visited[j] = true;
                        str += '0' +j+1;
                        break;
                    }
                    cnt ++;
                }
            }
        }
        for(j=n-1;j>=0;j--){
            if (!visited[j]) {
                str += '0' + j+1;
            }
        }
        return str;
        
    }
};


給定一個permutation中的序列,求字典序它的下一個permutation是什麼。

——邏輯推理

可以發現,下一個permutation可以這麼得來:

①當前permutation從後往前找到一直上升的子序列,假如一直上升到index_i

②找到index為i到end中最小的,比num[i-1]大的數字,記index為j,交換num[i-1],num[j]

③對num[i]~num[end]從小到大排序

PS:要注意有重複元素的情況e.g {1,5,1};

code:

class Solution {
public:
    void nextPermutation(vector<int> &num) {
        size_t n = num.size();
        int i = (int)n-1;
        int j=0;//find the position that stops increasing from tail
        while(num[i]<=num[i-1] && i>0)
            i--;
        
        sort(num.begin()+i, num.end());
        //find the digit that substitute(swap with) i
        for(j=i;j<n;j++){
            if (num[j]>num[i-1]) {
                break;
            }
        }
        if(i>0 && j<n)
            swap(num[i-1], num[j]);
    }
};

和第一題有細微的差別: 對於一個可能有重複元素的陣列輸出所有permutation。

——有條件dfs

想一下遞迴條件:

肯定還是遞迴,遞迴條件應該是如果當前list中已經出現過這幾個元素排列,就不要再加進去。

所以在第一題基礎上只加兩點:

1)對數組裡所有元素排序

2)對於上一次加到過list的相同元素(必然是在排序後陣列中與上一個相鄰元素相同的)不要再加

class Solution{
private:
    vector<vector<int> > L;
    vector<int> Nums;
    int l;
    vector<bool> visited;
public:
    void perm(vector<int>& list){
        if(list.size()==l){
            L.push_back(list);
            return;
        }
        for(int i=0; i<l; i++){
            if(!visited[i]){
                list.push_back(Nums[i]);
                visited[i] = true;
                perm(list);
                list.pop_back();
                visited[i] = false;
               
                while (i<l && Nums[i+1]==Nums[i]) {
                    i++;
                }
            }
        }
    }
    
    
    vector<vector<int> > permuteUnique(vector<int> &num){
        int i;
        Nums = num;
        l = Nums.size();
        sort(Nums.begin(),Nums.end());
        for(i=0;i<l;i++)
            visited.push_back(false);
        vector<int>list;
        perm(list);
        
        return L;
    }
};