Leetcode 第60排列序列两种解法

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。

示例 1:

输入:n = 3, k = 3
输出:“213”
示例 2:

输入:n = 4, k = 9
输出:“2314”
示例 3:

输入:n = 3, k = 1
输出:“123”

提示:
1 <= n <= 9
1 <= k <= n!
第一种无脑回溯,使用深度优先搜索把所有可能的组合枚举出来,然后每个组合放入一个vector容器里,返回指定序列

class Solution {
public:
    void dfs(vector<int> &s,int K,vector<bool> &path,int n,int &temp,int tar)
    {
        if(K==n)
        {
            s.push_back(temp);
            return;
        } 
        for(int i=0;i<n;i++)
        {
            if(path[i]==false)
            {
                temp=temp*10+i+1;
                path[i]=true;
                dfs(s,K+1,path,n,temp,tar);
                if(s.size()==tar) break;
                temp=temp/10;
                path[i]=false;
            }
        }
    }
    string getPermutation(int n, int k) 
    {
        vector<int> s;
        vector<bool> path(n,false);
        int temp=0;
        dfs(s,0,path,n,temp,k);
       // sort(s.begin(),s.end()); 这里dfs后s里本身就是有序的,不需排序;
        string ret;
        stringstream ss;
        ss<<s[k-1];
        ss>>ret;
        return ret;
    }

第二种是寻找其数学规律,先贴代码

 int F(int n,vector<int> &memo)
    {
        if(memo[n]!=0) return memo[n];
        else if(n==0 || n==1) return 1;
        int ret=F(n-1,memo)*n;
        memo[n]=ret;
        return ret;
    }
    string Ret(vector<int> &ret)
    {
        string s;
        int a=0;
        for(int i=0;i<ret.size();i++)
        {
            a=a*10+ret[i];
        }
        stringstream ss;
        ss<<a;
        ss>>s;
        return s;
    }
    string getPermutation(int n, int k) 
    {
        vector<int> path;
        for(int i=0;i<=n;i++) path.push_back(i);
        vector<int> ret;
        vector<int> memo(n+1,0);
        memo[0]=1;
        memo[1]=1;
        int N=n;
        while(ret.size()<N)
        {
            int s=(k%F(n-1,memo)==0)? k/F(n-1,memo):k/F(n-1,memo)+1;
            ret.push_back(path[s]);
            path.erase(path.begin()+s);
            if(ret.size()==N-1)
            {
                ret.push_back(path[path.size()-1]);
                break;
            }
            k=(k%F(n-1,memo)==0)? F(n-1,memo):k%F(n-1,memo);
            n--;
        }
        string ans=Ret(ret);
        return ans;
    }

其中函数F功能是求n的阶乘,带memo可以优化时间。string Ret是输出最终答案的字符串转化函数。
以n=4,k=9为例子,第一个数字可以使1,2,3,4的任一一种,每一种后面又有(n-1)!中排序,这里9/(n-1)!=9/3!=9/6=1,且9%6!=0,说明9不在为1位首的序列中,而是在第二个以2为首的序列中,这就是

int s=(k%F(n-1,memo)==0)? k/F(n-1,memo):k/F(n-1,memo)+1;

这句话的意思,要注意k%(n-1)==0的情况。s算出来是2,以2位首的的序列包含6中情况,他们分别对应k=7、k=8…k=12;此时k=9在这些序列里属于第三个排序,(7,8,9,10,11,12,9是第三个)

k=(k%F(n-1,memo)==0)? F(n-1,memo):k%F(n-1,memo);

这句话便是重定义k,因为此时题目变成了返回在剩下的数字(即1、3、4)所组成的排列里第k个序列,所以需要对k重新定义。现已知首个数字为2,剩下1、3、4三个数字,任何一个数字后面还有2!=2个排序,此时k=3,因此可以判断下个数字选取的是3。重复这些步骤,可以得到最终答案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值