【算法問題】尋找全排列的下一個數(shù)

尋找全排列的下一個數(shù)

摘自漫畫算法:

題目:給出一個正整數(shù),找出這個正整數(shù)所有數(shù)字全排列的下一個樹。說的通俗點就是在一個整數(shù)所包含數(shù)字的全部組合中,找到一個大于且僅大于原數(shù)的新整數(shù)。

例子:

  • 如果輸入12345,則返回12354
  • 如果輸入12354,則返回12435
  • 如果輸入12435,則返回12453

解題思路

在給出具體思路解法之前,先思考一個問題:由固定幾個數(shù)字組成的整數(shù),怎么排列最大?怎么排列最小?

解答:如果是固定的幾個數(shù)字,在逆序排列的情況下值最大,在順序排列的情況下值最小

例子:

給出1、2、3、4、5這幾個數(shù)字。最大組合為54321,最小組合為12345。

給出整數(shù)12354,它包含的數(shù)字是1、2、3、4、5,如何找到這些數(shù)字全排列之后僅大于原數(shù)的新整數(shù)呢?

為了和原數(shù)接近,我們需要盡量保持高位不變,低位在最小的范圍內(nèi)變換順序。至于變換順序的范圍大小,則取決于當(dāng)前整數(shù)的逆序區(qū)域。

圖1.png

如圖所示,12354的逆序區(qū)域是最后兩位,僅看這兩位已經(jīng)是當(dāng)前的最大組合。若想最接近原數(shù),又比原數(shù)更大,必須從倒數(shù)第3位開始改變。

怎樣改變呢?12345的倒數(shù)第3位是3,我們需要從后面的逆序區(qū)域中找到大于3的最小的數(shù)字,讓其和3的位置進(jìn)行互換。

如圖:

圖2.png

互換后的臨時結(jié)果是12453,倒數(shù)第3位已經(jīng)確定,這個時候最后兩位仍然是逆序狀態(tài)。我們需要把最后兩位轉(zhuǎn)變?yōu)轫樞驙顟B(tài),以此保證在倒數(shù)第3位數(shù)值為4的情況下,后兩位盡可能小。

圖3.png

這樣一來,就得到了想要的結(jié)果12435。

總結(jié):以上思路看起來復(fù)雜,其實只要3個步驟:

  • 從后向前查看逆序區(qū)域,找到逆序區(qū)域的前一位,也就是數(shù)字置換的邊界。
  • 讓逆序區(qū)域的前一位和逆序區(qū)域中大于它的最小數(shù)字交換位置。
  • 把原來的逆序區(qū)域轉(zhuǎn)為順序狀態(tài)。

這種解法有一個“高大上”的名字:字典序算法。

代碼實現(xiàn)

import java.util.Arrays;

/**
 * 描述:尋找全排列的下一個樹。
 * <p>
 * Create By ZhangBiao
 * 2020/6/7
 */
public class RangeNextNumber {

    public static int[] findNearestNumber(int[] numbers) {
        // 1、從后向前查看逆序區(qū)域,找到逆序區(qū)域的前一位,也就是數(shù)字置換的邊界
        int index = findTransferPoint(numbers);
        // 如果數(shù)字置換邊界是0,說明整個數(shù)組已經(jīng)逆序,無法得到更大的相同數(shù)字組成整數(shù),返回null
        if (index == 0) {
            return null;
        }
        // 2、把逆序區(qū)域的前一位和逆序區(qū)域中剛剛大于它的數(shù)字交換位置
        // 復(fù)制并入?yún)?,避免直接修改入?yún)?        int[] numbersCopy = Arrays.copyOf(numbers, numbers.length);
        exchangeHead(numbersCopy, index);
        // 3、把原來的逆序區(qū)域轉(zhuǎn)為順序
        reverse(numbersCopy, index);
        return numbersCopy;
    }

    private static int[] reverse(int[] num, int index) {
        for (int i = index, j = num.length - 1; i < j; i++, j--) {
            int temp = num[i];
            num[i] = num[j];
            num[j] = temp;
        }
        return num;
    }

    private static int[] exchangeHead(int[] numbers, int index) {
        int head = numbers[index - 1];
        for (int i = numbers.length - 1; i > 0; i--) {
            if (head < numbers[i]) {
                numbers[index - 1] = numbers[i];
                numbers[i] = head;
                break;
            }
        }
        return numbers;
    }

    private static int findTransferPoint(int[] numbers) {
        for (int i = numbers.length - 1; i > 0; i--) {
            if (numbers[i] > numbers[i - 1]) {
                return i;
            }
        }
        return 0;
    }

    private static void outputNumbers(int[] numbers) {
        for (int i : numbers) {
            System.out.print(i);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        // 打印12345之后的10個全排列整數(shù)
        for (int i = 0; i < 19; i++) {
            numbers = findNearestNumber(numbers);
            outputNumbers(numbers);
        }
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容