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.
在面試時需要注意咨詢面試官,輸入的k 是否小於1 或者 是否大於n!
分析:按照一般的遞歸求全排列的算法(LeetCode:Permutations),輸出的序列不是按字典序有序的,比如對於1,2,3,輸出序列為:
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
3 1 2
以3開頭的排列舉例,算法中是把1和3交換得到3 2 1,然后遞歸的求解,但是3 2 1不是以3開頭的最小序列,應該是3 1 2. 為了得到有序的序列,我們不是把1 3 交換,而是應該把3移動到1的前面,這樣得到的第一個以3開頭的序列就是3 1 2。因此有如下的算法:
算法1

1 class Solution { 2 private: 3 int k_; 4 string res_; 5 public: 6 string getPermutation(int n, int k) { 7 k_ = k; 8 string str = string("123456789").substr(0, n); 9 int cnt = 0; 10 PermutationRecur(str, 0, cnt); 11 return res_; 12 } 13 14 bool PermutationRecur(string &str, int index, int &cnt) 15 { 16 int len = str.size(); 17 if(index == len - 1) 18 { 19 cnt++; 20 if(cnt == k_) 21 { 22 res_ = str; 23 return true; 24 } 25 } 26 else 27 { 28 for(int i = index; i < len; i++) 29 { 30 rotate(&str[index], &str[i], &str[i+1]); 31 if(PermutationRecur(str, index + 1, cnt)) 32 return true; 33 rotate(&str[index], &str[i], &str[i+1]); 34 } 35 } 36 return false; 37 } 38 };
該算法在大數據下超時了。
算法2
利用next_permutation函數(該函數的詳解請參考LeetCode:Permutations算法3),這種做法也超時了

1 class Solution { 2 public: 3 string getPermutation(int n, int k) { 4 string str = string("123456789").substr(0, n); 5 for(int i = 1; i <= k-1; i++) 6 next_permutation(str.begin(), str.end()); 7 return str; 8 } 9 };
算法3
上面的算法都是逐個的求排列,有沒有什么方法不是逐個求,而是直接構造出第k個排列呢?我們以n = 4,k = 17為例,數組src = [1,2,3,...,n]。
第17個排列的第一個數是什么呢:我們知道以某個數固定開頭的排列個數 = (n-1)! = 3! = 6, 即以1和2開頭的排列總共6*2 = 12個,12 < 17, 因此第17個排列的第一個數不可能是1或者2,6*3 > 17, 因此第17個排列的第一個數是3。即第17個排列的第一個數是原數組(原數組遞增有序)的第m = upper(17/6) = 3(upper表示向上取整)個數。 本文地址
第一個數固定后,我們從src數組中刪除該數,那么就相當於在當前src的基礎上求第k - (m-1)*(n-1)! = 17 - 2*6 = 5個排列,因此可以遞歸的求解該問題。
1 class Solution { 2 public: 3 string getPermutation(int n, int k) { 4 string str = string("123456789").substr(0, n); 5 string res(n, ' '); 6 for(int i = 0; i < n; i++) 7 res[i] = helper(str, k); 8 return res; 9 } 10 //以s中字符構造的全排列中,返回第k個排列的第一個字符,並且刪除s中該字符 11 //s中字符遞增有序 12 char helper(string &s, int &k) 13 { 14 int tmp = factorial(s.size()-1), i = (k-1)/tmp; 15 char res = s[i]; 16 s.erase(i, 1); 17 k -= i*tmp;//更新k 18 return res; 19 } 20 //求正整數n的階乘 21 int factorial(int n) 22 { 23 int res = 1; 24 for(int i = 2; i <= n; i++) 25 res *= i; 26 return res; 27 } 28 };
當然也可以非遞歸實現
1 class Solution { 2 public: 3 string getPermutation(int n, int k) { 4 int total = factorial(n); 5 string candidate = string("123456789").substr(0, n); 6 string res(n,' '); 7 for(int i = 0; i < n; i++)//依次計算排列的每個位 8 { 9 total /= (n-i); 10 int index = (k-1) / total; 11 res[i] = candidate[index]; 12 candidate.erase(index, 1); 13 k -= index*total; 14 } 15 return res; 16 } 17 int factorial(int n) 18 { 19 int res = 1; 20 for(int i = 2; i <= n; i++) 21 res *= i; 22 return res; 23 } 24 };
【版權聲明】轉載請注明出處:http://www.cnblogs.com/TenosDoIt/p/3721918.html