Leetcode 60: Permutation Sequence

题目链接:

https://leetcode.com/problems/permutation-sequence/description/

描述

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):

“123”
“132”
“213”
“231”
“312”
“321”
Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

输出

返回一个字符串

样例输入

3 5

样例输出

”312”

算法思想:

这道题刚开始,我是使用组合数学中的生成排列算法的方法来解决的,提交之后虽然通过,但是时间复杂度为O( knlg(n) ),速度很慢。然后重新换了一种思路来写,不是通过上一个字符串来生成当前字符串的,而是直接生成当前字符串。通过找规律。
比如说n = 4,你有{1,2,3,4}
如果你列出你所有的排列

1 +(排列2,3,4)

2 +(排列1,3,4)

3 +(排列1,2,4)

4 +(排列1,2,3)

我们知道如何计算n个数字的排列数为 n!,所以3个数字意味着有6个可能的排列,也就是说4个数有24中排列。所以如果你要寻找(k = 14)第14个排列,那将是在 3 +(排列1,2,4)子集中寻找。用编程实现,你取k = 13(由于下标从0开始),并将其除以我们从阶乘得到的6,商就是所需的数字的索引。在阵列{1,2,3,4}中,k /(n-1)! = 13 /(4-1)! = 13/3! = 13/6 = 2。数组{1,2,3,4在索引2处的值为3,所以第一个数字是3。

其他后续数字的生成以此类似,因为不能重复,故需将上次已选择删除,使得可选数字规模减小。

{1,2,4}的排列将是:

1 +(排列2,4)

2 +(排列1,4)

4 +(排列1,2)

但是我们的k不再是第14个,因为在上一步中,我们已经从1和2开始排除了1、2的4个数字排列。所以你从k中减去1、2的排列数,编程实现如下:
k = k - (从前一个的索引)(n-1)! = k-2 (n-1)! = 13 - 2 *(3)! = 1

在第二个步骤中,2个数字的排列只有2种可能性,意味着上面列出的三个排列中的每一个有两种可能性,总共为6。我们正在寻找第一个(因为此时k = 1),所以这将在1 + (排列2,4)子排列中寻找。

意味着:要获取数字的索引是k /(n - 2)! = 1 /(4-2)! = 1/2! = 0 ..从{1,2,4},索引0为1

所以我们到目前为止的数字是3,1 ,然后重复。

{2,4}

k = k - (从过去的索引)(n-2)! = k-0 (n-2)! = 1 - 0 = 1;

第三个数字的索引= k /(n-3)! = 1 /(4-3)! = 1/1! = 1 …从{2,4},索引1有4

第三个数是4

{2}

k = k - (从过去的索引)(n-3)! = k - 1 (4 - 3)! = 1 - 1 = 0;

第四个数字的索引= k /(n-4)! = 0 /(4-4)! = 0/1 = 0,从{2},索引0有2

第四个数字是2

故返回3142.

生成排列源代码

/*
Author:杨林峰
Date:2017.10.27
LeetCode(60):Permutation Sequence
*/
class Solution {
public:
    string getPermutation(int n, int k) {
        string str;
        for (int i = 1; i <= n; i++)
        {
            str += i + '0';
        }
        int cnt = 1;
        while (cnt < k)
        {
            int pos1, pos2;
            //找出左边数字比右边小的,并记录其位置
            for (int i = n - 2; i >= 0; i--)
            {
                if (str[i] < str[i + 1])
                {
                    pos1 = i;
                    break;
                }
            }
            int temp = str[pos1 + 1];
            //找出pos1右边比temp大的数但是最接近temp的数,记录其位置
            for (int i = pos1 + 1; i < n; i++)
            {
                if (str[i] > str[pos1] && str[i] <= temp)
                {
                    temp = str[i];
                    pos2 = i;
                }
            }
            //交换
            swap(str[pos1], str[pos2]);
            //对后面的数从小到大排序
            sort(str.begin() + pos1 + 1, str.end());
            //cout << str << endl;
            cnt++;
        }
        cout << str << endl;
        return str;
    }
};

最优源代码

/*
Author:杨林峰
Date:2017.10.27
LeetCode(60):Permutation Sequence
*/
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
int factorial[11];
string getPermutation(int n, int k)
{
    int pos = 0;
    int sum = 1;
    factorial[0] = 1;
    //生成阶乘数
    for (int i = 1; i <= n; i++)
    {
        sum *= i;
        factorial[i] = sum;

    }
    vector<int> vec, vec1;  //vec为初始值,vec1为生成的结果值
    //初始化vec
    for (int i = 1; i <= n; i++)
    {
        vec.push_back(i);
    }
    k--;
    //去生成结果的每一位数
    for (int i = 1; i <= n; i++)
    {
        int index = k / factorial[n - i];
        vec1.push_back(vec[index]);
        vec.erase(vec.begin() + index);
        k -= index*factorial[n - i];
    }
    //将数字转换成字符串
    string str;
    for (vector<int>::iterator it = vec1.begin(); it != vec1.end(); it++)
    {
        str += (*it) + '0';
    }
    return str;
}
int main()
{
    int n, k;
    while (cin >> n >> k)
    {
        string str = getPermutation(n, k);
        cout << str << endl;
    }
    return 0;
}

算法复杂度:

由源代码可知,第一种算法时间复杂度为O(knlogn),因为大循环要循环k次,循环里复杂度最大的是排序操作,故时间复杂度为O(knlogn),第二种算法的时间复杂度为O(n^2),因为vector擦除元素时间复杂度为O(n),要循环n次来生成n个数,故时间复杂度为O(n^2)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章