面試算法題(1)

原文鏈接:https://blog.csdn.net/kexuanxiu1163/article/details/99024362

版權聲明:本文爲博主原創文章,遵循 CC 4.0 by-sa 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/kexuanxiu1163/article/details/99024362

題目描述
給你數字 0 ,1 ,2 ,那麼所有排列從小到大就會是 012,021,102,120,201,210,那麼如果給你 0,1,2,3,4,5,6,7,8,9 讓你去排,從小到大排在第一百萬的數是多少?

題目地址:https://projecteuler.net/problem=24

題目解析
寫普通算法程序題寫多了,見到 排列 首當其衝想到用深度優先搜索,最後發現如果把所有的排列可能都給列出來,然後排序,這樣搞下來程序運行的時間複雜度是 O(n!),比指數的時間複雜度還要高!

如果從數學思路上去思考的話,那問題就簡單了!

通過一個數一個數的去選。

首先第一個位置(最高位)有十種可能性,我們可以利用 10! / 10 來計算出每一個數字打頭有多少種排列,然後用 一百萬 / (10! / 10) 來決定我們要選排在第幾的數,選中一個數後,剩下的數就只有 9 個,將 10 換成 9,重複上面的操作,當然,一百萬這個值也得根據我們找的數來做更新,比如你第一個數選擇了 2,那麼 0 和 1 打頭的 2 * 10! / 10 種組合都可以減掉,剩下的我們需要繼續在後面的 9 個數的排列中找。

//@author:P.yhpublic static void main(String[] args) {    System.out.println(lexicographicPermu(10, 1000000L));}private static String lexicographicPermu(int numberOfDigit, long target) {    int copyOfNumerOfDigit = numberOfDigit;    long copyOfTarget = target;    // list 裏面存放的是所有的可以選擇的情況,並從大到小排列    // 這裏是 0,1,2,3,4,5,6,7,8,9    List<Integer> candidate = new ArrayList<>();    for (int i = 0; i < copyOfNumerOfDigit; ++i) {        candidate.add(i);    }    String result = "";    // 只要沒有選完,就繼續選擇    while (copyOfNumerOfDigit != 0) {        long totalPermutation = 1;        int n = copyOfNumerOfDigit;        // 計算 n!        // 例如,選擇第一個數,那 totalPermutation 就是 10!        while (n != 1) {            totalPermutation *= n;            n--;        }        // 計算選擇當前情況下的第幾個數        // 這裏 copyOfTarget - 1 是爲了防止整除的情況        // 舉 0,1,2 這個例子,當我們要選擇排在第 2 的那個數,也就是 021        // 選第一個數的時候, totalPermutation = 3 * 2 * 1 = 6, copyOfNumberOfDigit = 3        // 2 / (6 / 3) = 1,顯然不符合要求,(2 - 1) / (6 / 3) = 0 纔是選中了 0        int selection = (int)((copyOfTarget - 1) / (totalPermutation / copyOfNumerOfDigit));        result += candidate.remove(selection);        // 更新下一次要選擇第幾個數        copyOfTarget -= (totalPermutation / copyOfNumerOfDigit) * selection;        // 選了一個數,可選項相應減少一        copyOfNumerOfDigit--;        copyOfTarget = copyOfTarget <= 0 ? 0 : copyOfTarget;    }    return result;}
public static void main(String[] args) {
    System.out.println(lexicographicPermu(10, 1000000L));
}

private static String lexicographicPermu(int numberOfDigit, long target) {
    int copyOfNumerOfDigit = numberOfDigit;
    long copyOfTarget = target;

    // list 裏面存放的是所有的可以選擇的情況,並從大到小排列
    // 這裏是 0,1,2,3,4,5,6,7,8,9
    List<Integer> candidate = new ArrayList<>();
    for (int i = 0; i < copyOfNumerOfDigit; ++i) {
        candidate.add(i);
    }

    String result = "";

    // 只要沒有選完,就繼續選擇
    while (copyOfNumerOfDigit != 0) {
        long totalPermutation = 1;
        int n = copyOfNumerOfDigit;

        // 計算 n!
        // 例如,選擇第一個數,那 totalPermutation 就是 10!
        while (n != 1) {
            totalPermutation *= n;
            n--;
        }

        // 計算選擇當前情況下的第幾個數
        // 這裏 copyOfTarget - 1 是爲了防止整除的情況
        // 舉 0,1,2 這個例子,當我們要選擇排在第 2 的那個數,也就是 021
        // 選第一個數的時候, totalPermutation = 3 * 2 * 1 = 6, copyOfNumberOfDigit = 3
        // 2 / (6 / 3) = 1,顯然不符合要求,(2 - 1) / (6 / 3) = 0 纔是選中了 0
        int selection = (int)((copyOfTarget - 1) / (totalPermutation / copyOfNumerOfDigit));
        result += candidate.remove(selection);

        // 更新下一次要選擇第幾個數
        copyOfTarget -= (totalPermutation / copyOfNumerOfDigit) * selection;

        // 選了一個數,可選項相應減少一
        copyOfNumerOfDigit--;
        copyOfTarget = copyOfTarget <= 0 ? 0 : copyOfTarget;
    }

    return result;
}


--------------------- 
版權聲明:本文爲CSDN博主「程序員吳師兄」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/kexuanxiu1163/article/details/99024362

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