結合力扣第60題,第k個排列,進行康托展開和逆康托展開的學習。
題目描述:給出集合[1,2,…,n],其所有元素有n!種排列,按大小排列出所有情況,並一一標記,當n=3時,排列如下:
「123」
「132」
「213」
「231」
「312」
「321」
給定n和k,返回第k個排列
如n=3,k=3 則返回"213"
講述康托編碼之前,需要先學習一下康托展開,康托展開是乙個全排列到自然數的乙個雙射,說白了就是將一堆數從小到大排列,然後計算比某個排列數小的排列數有多少個,或者加1來計算某個排列數所在的位置。
公式:ai,表示的是比當前所在位數i的數字小的,並且還未使用的數字的個數,有點拗口哈,舉個例子:
給定n=5,即集合(1,2,3,4,5),給定排列34152,求比它小的排列有多少(或者說求該數值排在第幾個,即求x+1)。
第乙個數3,第5位數,即n=5,比他小的有1和2,2個,所以an = a5 = 2;
第二個數4,第4位數,即n=4,比他小的有1,2,3,有3個數字,但是反看上一位數字是3,所以1和2必定會被使用乙個,所以只剩下1,3或者2,3可以選,這兩種情況可選的數字個數都為2,所以an=a4=2,通過這裡再來理解ai的意思,比所在位數4的數字4小,並且還未被使用的數字(1,3或者2,3)的個數
第三個數字1,第3位,即n=3,比1小的數的個數為0,所以an=a3=0;
第四個數5,第2位,即n=2,比他小的有1,2,3,4四個數,但是第乙個數3,比他小的為1,2,使用了其中乙個,第二個數4,比他小的為1,2,3,會使用1,3或者2,3兩個,這樣下來1,2,3全部都被使用,所以比5小的數字就剩4這個數字,所以比5下的數字的個數為1,即an=a2=1;
最後乙個數2,第1位,即n=1,比2小還未使用的數字個數為0,所以an=a1=0
根據公式得x=2×(5-1)!+2×(4-1)!+0×(3-1)!+1×(2-1)!+0×(1-1)!=61,所以比34152小的數有61個,它所在的位置即61+1=62
既然康托展開是個雙射,那麼它也可以根據x(位置,或者第幾大的排列號)和n,反向求出該排列數。
還是上面的例子,知道x=61(或者知道該排列數在第62位,那麼比他小的就有62-1=61個),和n=5的情況下,求出該排列數34152
61/(5-1)! 得2 餘13 ,所以可以知道,比他小的數有2個,那麼第乙個數為3;
13/(4-1)! 得2 餘1,所以比第二個數小的也有兩個,但是3已經被選用了,所以該數為4(比4小的為1和2,兩個數);
1/(3-1)! 得0餘1,比第三個數小的數的個數為0,所以該數為1;
1/(2-1)! 得1餘0,所以比第四個數小的數的個數為1,由於1,3,4均已經使用,所以只剩下2和5,從未使用的數字中選擇,所以該數為5(2比5小,所以比他小的數的個數為1,如果該數為2,那麼比他小的數為0)
最後剩下乙個數2。
所以綜上,得出結果34152
懂了康托展開和逆康托展開後,再來看剛開始那道題,就容易了,直接使用逆康托展開即可,**如下:
在這裡插入**片
class
solution
:def
getpermutation
(self, n:
int, k:
int)
->
str:
import math
res=
"" n_list=
[str
(i)for i in
range(1
,n+1)]
k=k-
1while n>0:
n=n-
1 a,k=
divmod
(k,math.factorial(n)
) res+=n_list.pop(a)
return res
LeetCode 第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 ...
力扣 060 第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 ...
Leetcode題目60 第k個排列
給出集合 1,2,3,n 其所有元素共有 n 種排列。按大小順序列出所有排列情況,並一一標記,當 n 3 時,所有排列如下 123 132 213 231 312 321 給定 n 和 k,返回第 k 個排列。說明 給定 n 的範圍是 1,9 給定 k 的範圍是 1,n 示例 輸入 n 3,k 3 ...