leetcode-第k个排列-康托展开和康托逆展开

Leetcode-第60题,分类在回溯算法中,但是回溯并不是最佳解,甚至会超时。
leetcode-第k个排列-康托展开和康托逆展开_第1张图片
乍一眼看,这又是一个类似于全排列的回溯题,第一思路是又使用回溯法递归出全排列的所有情况列一个表,然后根据k来找到第k个排列。
很遗憾的是这种方法,百分之百超时,效率太低,即便是添加了一些到达k就返回的剪枝,效率也很难令人满意。

事实上,使用纯数学的方法才是这题的解法,我们只想要第k个排列,不需要无谓的全盘列出。
题目需要第k个排列,那么排列的规律一定是确定的,这就引入了康托展开–Cantor expansion
这个名字听起来像个高深莫测的数学定理,但是其实就是对全排列与排列顺序关系的数学规则表示:
在这里插入图片描述
这个网上很多讲解:https://blog.csdn.net/ltrbless/article/details/87696372
比方说{1,2,3}的第3个是213:
”2“前面有个1,那么前面有1*(3-1)!=2种排列;
”1“前面啥都没有,那么前面有0*(3-2)!=0种排列;
”3“前面也啥都没有了,那么前面有0*(3-3)!=0种排列;
所以”213“排在第3位;
那怎么逆推呢?
3-1=2,说明前面有2种情况;
2/(3-1)!,商1余0,说明前面有1个,自己在其后,那么在{1,2,3}挑出2来l;
0/(3-2)!,商0余0,说明前面没有,自己在剩下的第一个,那么在{1,3}中挑出1;
0/(3-3)!,商0余0,说明前面没有,自己在剩下的第一个,那么在{3}中挑出3;
推知k=3时,排列为”213“

直接上代码:

class Solution {
public:
    string getPermutation(int n, int k) {
        int step=0;
        k--;
        int list[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//1-9阶乘的情况直接列表下来
        string res = "";

        int flag[10] = {0};
        while(step!=n)
        {
            int residue = k%(list[n-step-1]);//余数
            int quot = k/(list[n-step-1]);//商
            for(int i=1;i<n+1&&quot>=0;i++)
            {
                if(flag[i]==0)
                {
                    if(quot==0)
                    {
                        flag[i]=1;
                        res.push_back(char(i+48));
                        break;
                    }
                    quot--;
                }
            }
            k = residue;
            step++;
        }
        return res;
    }
};

leetcode-第k个排列-康托展开和康托逆展开_第2张图片

你可能感兴趣的:(leetcode)