字節尿性,康託展開求第K個排列!

字節尿性,康託展開求第K個排列!


今天是小浩算法 「365刷題計劃」 第 109 天。繼續爲你們講解 leetcode 第 60 題,是一道中等難度的題目。算法

排列類別的問題主要就兩個,一個是全排列:app

小白真能看一篇文章就學會全排列算法嗎?ide

另外一個就是本題(兩個題在劍指offer都出現了):ui

字節尿性,康託展開求第K個排列!

01

PART

第K個排列


題目比較繞,耐心點仍是能夠看懂的~加油!3d



題目:給出集合 [1,2,3,…,n],其全部元素共有 n! 種排列。code

按大小順序列出全部排列狀況,並一一標記,當 n = 3 時, 全部排列以下:blog

"123"leetcode

"132"rem

"213"get

"231"

"312"

"321"

給定 n 和 k,返回第 k 個排列。

說明:

給定 n 的範圍是 [1, 9]。

給定 k 的範圍是[1, n!]。


給出兩個示例:

示例 1:

輸入: n = 3, k = 3 輸出: "213"

字節尿性,康託展開求第K個排列!

示例 2:

輸入: n = 4, k = 9 輸出: "2314"

02

PART

題解分析


這道題目的標籤標的數學和回溯算法,因此咱們分別用數學和回溯的方法來解題。


從數學方法提及,先講一下康託展開。

那康託展開是幹嗎的?用來計算當前排列在全部由小到大全排列中的順序。臥槽,不就是本題嗎。

聽不懂?就是說若是你知道 1234 是序號 1,1243 是 序號2,這個公式就能夠直接告訴你 4132 的序號是多少!

公式是這樣的:

字節尿性,康託展開求第K個排列!

解釋一哈:

這個 X 就是最終的序號值,比實際序號少一位,由於能夠看到康託展開第一位計算的值是 0 。

字節尿性,康託展開求第K個排列!

網上看到的不少圖可能名次是從 1 開始,這個你們注意一下就行:

字節尿性,康託展開求第K個排列!

關鍵是這個 字節尿性,康託展開求第K個排列! ,這個其實就是看原數的第 i 位在當前未出現的元素中是排在第幾個。

感受這句話有點拗口,用上面出現的問題解釋一下:1234 是序號 1,1243 是 序號 2, 4132 的序號是多少?

解釋:第一個數是 4,比 4 小的而且尚未出現過的數有 3 個(123),第二個數是 1,比 1 小的而且尚未出現過的數爲 0 個。第三個數是 3,比 3 小的而且尚未出現過的數爲 1。第四個數是 2 ,比 2 小的而且尚未出現過的數爲 0 個。

有 X = 33!+ 02!+ 11!+ 00!= 19,此時的序號爲 19+1 = 20。還不明白的看下下面這個圖:

字節尿性,康託展開求第K個排列!

而後咱們把上面的東西反着來,就叫作 逆康託展開。換句話說,就是給咱們了 X 的值,讓咱們求 。

對於 逆康託展開,我仍是給一個例子。好比 nums 仍是 1234,咱們要找第 15 位。那麼要進行如下幾步:

  • 首先,由於 X 比實際序號少一位,因此咱們要用實際序號減1,也就是 15 - 1 = 14。

  • 目標值的第一個字符,14 / 3! = 2 ... 2 (商2餘2),沒有已使用的字符,第一個字符取在未使用的字符中排增序第3。即3

  • 目標值的第二個字符,2 / 2! = 1 ... 0,已使用的字符爲3,第二個字符取在未使用的字符中增序排第2。即2

  • 目標值的第三個字符,0 / 1! = 0 ... 0,已使用的字符爲2和3,第三個字符取在未使用的字符中增序排第1。即1

  • 目標值的第三個字符,0 / 0! = 0 ... 0,已使用的字符爲1,2和3,第四個字符取在未使用的字符中增序排第1。即4

  • 那麼要求的這個序列爲:3214。

  • 能夠查下上面的表,發現是正確的。若是看不懂,能夠回到上面的例子再看看,其實就是把上面過程倒着來。

最後,咱們再將逆康託展開進行應用:

//JAVA
class Solution {
    public String getPermutation(int n, int k) {
        StringBuilder sb = new StringBuilder();
        // 候選數字
        List<Integer> candidates = new ArrayList<>();
        // 分母的階乘數
        int[] factorials = new int[n+1];
        factorials[0] = 1;
        int fact = 1;
        for(int i = 1; i <= n; ++i) {
            candidates.add(i);
            fact *= i;
            factorials[i] = fact;
        }
        //預處理
        k -= 1;
        for(int i = n-1; i >= 0; --i) {
            // 計算候選數字的index
            int index = k / factorials[i];
            sb.append(candidates.remove(index));
            k -= index*factorials[i];
        }
        return sb.toString();
    }
}

字節尿性,康託展開求第K個排列!

03

PART

類似題目


說是類似題目,可是其實下面兩個題目我都是用回溯來求解的啦。算是通用解法吧~你們有興趣也能夠用回溯來解下本題。

小白學着求全排列

小白學着求子集(快手)

我把我寫的全部題解都整理成了一本電子書,每道題都配有完整圖解。

相關文章
相關標籤/搜索