LeetCode | Permutation Sequence(找到全排列中的第k个排列)


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.

方案一:

可以一个一个去求解,利用DFS,来找。不过会超时

class Solution {
private:
    int a[10];
    bool canUse[10];
    string ret;
public:
    void dfs(int dep, int maxDep, int &k)
    {
        if (k == 0)
            return;
            
        if (dep == maxDep)
        {
            k--;
            if (k == 0)
            {
                ret = "";
                for(int i = 0; i < maxDep; i++)
                    ret += (char)(a[i] + '0');
                return;
            }
        }
        
        for(int i = 1; i <= maxDep; i++)
            if (canUse[i])
            {
                canUse[i] = false;
                a[dep] = i;
                dfs(dep + 1, maxDep, k);
                canUse[i] = true;
            }
    }
    
    string getPermutation(int n, int k) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        memset(canUse, true, sizeof(canUse));
        
        dfs(0, n, k);
        
        return ret;
    }
};

方案二:

利用数学的方法,比如题目中找到第5个数,其实就是5/(2!),让1和3交换,然后递归一位再找第5%(2!)个数。

但有个小问题,当k%(j!)为0的时候,就要交换后,将剩余的数据翻转。知道了这些,就能写出代码,不过有些繁琐

class Solution {
public:
    string getPermutation(int n, int k) {
        string str;
        int arr[9] = {1,2,3,4,5,6,7,8,9};
        if(n<=0 || k<=0)
            return str;

        int fact = NFact(n);
        if(k>fact)
            k = k%fact;
        fact = fact/n;
        int tmp = n-1;  //表示要除数
        int index = 0;  //指示在index位调整
        while(k){
            while(k<fact){  //跳过不用更改的位置
                fact = fact / tmp;
                tmp--;
                index++;
            }
            if(k%fact == 0){    //当k%fact == 0与不等于0,执行的情况不一样,分开考虑
                int i = k/fact-1;
                Swap(arr,index,index+i);
                Ajust(arr,index+1,n-1);
                break;
            }else{
                int i = k/fact;
                Swap(arr,index,index+i);
                k = k%fact;
            }
        }
        for(int i = 0;i < n;i++)
            str.push_back(arr[i]+'0');
        return str;
    }
    //求总的个数
    int NFact(int n){
        if(n==0)
            return 1;
        return n*NFact(n-1);
    }
    //调整,将end的元素移动到begin处,并且begin...end-1平移
    void Swap(int arr[],int begin,int end){
        int temp = arr[end];
        for(int i = end;i > begin;i--){
            arr[i] = arr[i-1];
        }
        arr[begin] = temp;
    }
    //当k%fact==0的时候,调整过以后,后面的数翻转
    void Ajust(int arr[],int begin,int end){
        while(begin < end){
            int temp = arr[begin];
            arr[begin] = arr[end];
            arr[end] = temp;
            begin++;
            end--;
        }
    }
};

方案三:

在n!个排列中,第一位的元素总是(n-1)!一组出现的,也就说如果p = k / (n-1)!,那么排列的最开始一个元素一定是nums[p]。

假设有n个元素,第K个permutation是
a1, a2, a3, …..   …, an
那么a1是哪一个数字呢?
那么这里,我们把a1去掉,那么剩下的permutation为
a2, a3, …. …. an, 共计n-1个元素。 n-1个元素共有(n-1)!组排列,那么这里就可以知道
设变量K1 = K
a1 = K1 / (n-1)!
同理,a2的值可以推导为
a2 = K2 / (n-2)!
K2 = K1 % (n-1)!
 …….
a(n-1) = K(n-1) / 1!
K(n-1) = K(n-2) /2!
an = K(n-1)

string getPermutation(int n, int k) {
        vector<int> nums(n);
        int pCount = 1;
        for(int i = 0 ; i < n; ++i) {
            nums[i] = i + 1;
            pCount *= (i + 1);
        }

        k--;
		string res = "";
        for(int i = 0 ; i < n; i++) {
			pCount = pCount/(n-i);
            int selected = k / pCount;
			res += ('0' + nums[selected]);
            
            for(int j = selected; j < n-i-1; j++)
                nums[j] = nums[j+1];
            k = k % pCount;
        }
        return res;
    }


点赞