第K個排列
給出集合 [1,2,3,…,n],其所有元素共有 n! 種排列。
按大小順序列出所有排列情況,并一一標記,當 n = 3 時, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
給定 n 和 k,返回第 k 個排列。說明:
給定 n 的范圍是 [1, 9]。
給定 k 的范圍是[1, n!]。示例 1:
輸入: n = 3, k = 3
輸出: "213"示例 2:
輸入: n = 4, k = 9
輸出: "2314"
題目分析
1. 我一開始想到的是全排列的函數的復用,我能找到全排列難道我找不到第k個排列?但我發現全排列那里并沒有按序排列,因此不可復用..
2. 可以復用 下一個排列 的函數,畢竟一直找找到第K個就好,效率較低,沒嘗試。
3. 數學計算
全排列每個位置的每個數其實是有數學特征的。
比如對 1 2 3
3.1 首位情況: 肯定先是1在首位兩次,然后是2首位2次,然后是3首位兩次。
3.2 次位情況:除掉首位數字,剩下的數字在nums[]中,同樣也是先nums[0]作為次位,出現 尾部排列次數 次,然后是nums[1]...依次類推。
綜上:即我們知道每位的次數情況,比如 1 2 3中,我們要找第5個排列,因為首位1在前兩個, 首位2 在3 4 個,因此 第5個排列必然是首位3。
再看次位,第5個排列,除去 1 2 首位的四個,我們要找的是3首位的第一個,依次類推即可。
即我們可以直接計算出每一位應該是什么數字。然后組成result即可。
4.細節
1.當前位 每個數字出現次數,由尾部的全排列次數決定,而全排列是n!,因此最好能預置個階乘結果數組。
- k/fac[n-1] 向上取整得i,此時該位應該是 nums里的第i個數(下標i-1),同時對nums刪除這個數。
- n==1 時,nums只剩一個數,直接連上并返回。
題解代碼
class Solution {
public:
string getPermutation(int n, int k)
{
//題解數組
const vector<int> fac = {0,1,2,6,24,120,720,5040,40320,362880,3628800};
string result(n,'0');
string nums(n,'0');
for(int i=0;i<n;i++)
nums[i]='1'+i;
for(int i=0;n>0;i++)
{
int a;
int left=0;
if(n>=2) //計算每個首位有多少個排列,跳過這些排列
{
left=fac[n-1];
a=k/left;
if(k%left!=0)
a++;
result[i]=nums[a-1];
k-=(a-1)*left;
nums.erase(a-1,1);
n--;
}
else //只剩一個數,直接修改返回
{
result[i]=nums[0];
return result;
}
}
return result;
}
};