leetcode60. Permutation Sequence

medium程度题

题目:

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"
容易想到的是调用k - 1次上一题leetcode31. Next Permutation的函数,复杂度O( (K - 1) * N )
参考别人的解法,知道了康托展开
康托展开用来求一个数是全排列的第几小数
如 52314
比5小的数有4个
比2小的数有1个
比3小的数有2个,因为2已经在左边固定了,所以有1个
比1小的数没有
比4小的数有三个,1,2,3在左边都已经固定,故有0个
K = 4 * 4! + 1 * 3! + 1 * 2! + 0 * 1! + 0 * 0!
因此52314是{1,2,3,4,5}全排列的第 K + 1 = 105小的数

反过来已知n,K求排列,康托逆展开,过程如下
K - 1 = 104
104 / 4! =  4 103 % 4!=  8
       8 / 3! = 1  8 % 3!  =  2
       2 / 2! = 1  1 % 2!  =  0
       0 / 0! = 0  0 % 0!  =  0
       0 / 0! = 0  0 % 0!  =  0
比4大的数为5
比1大的数为2
比1大的数为2,因为2已经出现故为3
比0大的数为1
比0大的数为1,因为1,2,3已经出现,故为4
所以这个数为52314

我的解法是构建一个used数组用来记录数字是否被使用过,假如当前求得第i位的商为K
那么在序列{1,2,3...n}中,从前往后第K个未被使用的数就是第i位的值,因为我们要确保
第i + 1位到第n位恰好有K个数小于第i位的数

复杂度为O(n ^2)
AC解

class Solution {
public:
    string getPermutation(int n, int k) 
    {
        string s(n,'0');
        for (int i = 0; i <= n; i++)
            s[i] += i + 1;
        return kth_permutation(s,n,k);
    }
    //求阶乘
    int fac(int n)
    {
        int result = 1;
        for (int i = 1; i <= n; i++)
            result *= i;
        return result;
    }
    
    string kth_permutation(string s,int n,int k)
    {
        string result;
        int base = fac(n - 1);
        k--;
        bool used[n + 1];
        for (int i = 1; i < n + 1; i++)
            used[i] = false;
        for (int i = 1; i < n; i++)
        {
            int m = k / base;
            k %= base;
            base /= (n -i);
            //从used数组中找m个未使用的数,第m + 1个未使用的数就是第i位的值
            int a = 0,index;
            for (int i = 1; i < n + 1; i++)
            {
                if (!used[i])
                    a++;
                if (a == m + 1)
                {
                    index = i;
                    break;
                }
            }
            result.push_back(s[index - 1]);
            used[index] = true;
        }
        //最后一个未被使用的数放入字符串尾
        int a;
        for (int i = 1; i < n + 1; i++)
            if (!used[i])
            {
                a = i - 1;
                break;
            }
        result.push_back(s[a]);
        return result;
    }
};
参考了别人的解法,复杂度为O(n),真是精妙
AC解:
class Solution {
public:
    string getPermutation(int n, int k) 
    {
        string s(n,'0');
        for (int i = 0; i <= n; i++)
            s[i] += i + 1;
        return kth_permutation(s,n,k);
    }
    
    int fac(int n)
    {
        int result = 1;
        for (int i = 1; i <= n; i++)
            result *= i;
        return result;
    }
    
    string kth_permutation(string s,int n,int k)
    {
        string result;
        int base = fac(n - 1);
        k--;
        for (int i = n - 1; i > 0; k %= base,base /= i,i--)
        {
            int m = k / base;
            
            result.push_back(*(s.begin() + m));
            s.erase(s.begin() + m);
        }
        result.push_back(s[0]);
        return result;
    }
};

直接将使用了的擦除,避免了每次对数组进行搜索,极大的降低了复杂度,妙!
路漫漫其修远兮,中途试了n次,改了几个小时才想到最上面的解法,别人的解法是多么
的优美,要多思考,多总结,多练啊!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值