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

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