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

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

摘自漫畫算法:

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

例子:

  • 如果輸入12345污朽,則返回12354
  • 如果輸入12354,則返回12435
  • 如果輸入12435龙考,則返回12453

解題思路

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

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

例子:

給出1、2肛宋、3州藕、4、5這幾個(gè)數(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

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

圖3.png

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

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

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

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

代碼實(shí)現(xiàn)

import java.util.Arrays;

/**
 * 描述:尋找全排列的下一個(gè)樹板驳。
 * <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慨蓝,說明整個(gè)數(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個(gè)全排列整數(shù)
        for (int i = 0; i < 19; i++) {
            numbers = findNearestNumber(numbers);
            outputNumbers(numbers);
        }
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末此熬,一起剝皮案震驚了整個(gè)濱河市谱秽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌摹迷,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郊供,死亡現(xiàn)場離奇詭異峡碉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)驮审,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門鲫寄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疯淫,你說我怎么就攤上這事地来。” “怎么了熙掺?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵未斑,是天一觀的道長。 經(jīng)常有香客問我币绩,道長蜡秽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任缆镣,我火速辦了婚禮芽突,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘董瞻。我一直安慰自己寞蚌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布钠糊。 她就那樣靜靜地躺著挟秤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眠蚂。 梳的紋絲不亂的頭發(fā)上煞聪,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音逝慧,去河邊找鬼昔脯。 笑死啄糙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的云稚。 我是一名探鬼主播隧饼,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼静陈!你這毒婦竟也來了燕雁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鲸拥,失蹤者是張志新(化名)和其女友劉穎拐格,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刑赶,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捏浊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撞叨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片金踪。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖牵敷,靈堂內(nèi)的尸體忽然破棺而出胡岔,到底是詐尸還是另有隱情,我是刑警寧澤枷餐,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布靶瘸,位于F島的核電站,受9級特大地震影響尖淘,放射性物質(zhì)發(fā)生泄漏奕锌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一村生、第九天 我趴在偏房一處隱蔽的房頂上張望惊暴。 院中可真熱鬧,春花似錦趁桃、人聲如沸辽话。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽油啤。三九已至,卻和暖如春蟀苛,著一層夾襖步出監(jiān)牢的瞬間益咬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工帜平, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幽告,地道東北人梅鹦。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像冗锁,于是被迫代替她去往敵國和親齐唆。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354