乘風破浪:LeetCode真題_031_Next Permutation

乘風破浪:LeetCode真題_031_Next Permutation

1、前言

    這是一道經典的題目,咱們實在想不出最好的方法,只能按照已有的方法來解決,同時咱們也應該思考一下爲何要這樣作?是怎麼想到的?這比咱們記住步驟更加的有用。
java

2、Next Permutation

2.1 問題

2.2 分析與解決

     排列(Arrangement),簡單講是從N個不一樣元素中取出M個,按照必定順序排成一列,一般用A(M,N)表示。當M=N時,稱爲全排列(Permutation)。從數學角度講,全排列的個數A(N,N)=(N)*(N-1)*...*2*1=N!,但從編程角度,如何獲取全部排列?那麼就必須按照某種順序逐個得到下一個排列,一般按照升序順序(字典序)得到下一個排列。
     例如對於一個集合A={1,2,3,},首先獲取全排列a1: 1,2,3,;而後獲取下一個排列a2: 1,3,2,;按此順序,A的全排列以下:
算法

a1: 1,2,3;  a2: 1,3,2;  a3: 2,1,3;  a4: 2,3,1;  a5: 3,1,2;  a6: 3,2,1;  共6種。

    對於給定的任意一種全排列,若是能求出下一個全排列的狀況,那麼求得全部全排列狀況就容易了。好在STL中的algorithm已經給出了一種健壯、高效的方法,下面進行介紹。編程

/**
 * current: 3   7  6  2  5  4  3  1  .
 *                    |  |     |     |
 *          find i----+  j     k     +----end
 * swap i and k :
 *          3   7  6  3  5  4  2  1  .
 *                    |  |     |     |
 *               i----+  j     k     +----end
 * reverse j to end :
 *          3   7  6  3  1  2  4  5  .
 *                    |  |     |     |
 *          find i----+  j     k     +----end
 * */
1  具體方法爲:
2 a)從後向前查找第一個相鄰元素對(i,j),而且知足A[i] < A[j]。易知,此時從j到end必然是降序。能夠用反證法證實,請自行證實。
3 b)在[j,end)中尋找一個最小的k使其知足A[i]<A[k]。因爲[j,end)是降序的,因此必然存在一個k知足上面條件;而且能夠從後向前查找第一個知足A[i]<A[k]關係的k,此時的k必是待找的k。
4 c)將i與k交換。
5     此時,i處變成比i大的最小元素,由於下一個全排列必須是與當前排列按照升序排序相鄰的排列,故選擇最小的元素替代i。易知,交換後的[j,end)仍然知足降序排序。由於在(k,end)中必然小於i,在[j,k)中必然大於k,而且大於i。
6 d)逆置[j,end)
7      因爲此時[j,end)是降序的,故將其逆置。最終得到下一全排序。
8 e) 結束
9     若是在步驟a)找不到符合的相鄰元素對,即此時i=begin,則說明當前[begin,end)爲一個降序順序,即無下一個全排列,STL的方法是將其逆置成升序。

    經過上面的描述,咱們能夠進行一次全排列的算法,就會發現真的很是的有用,那麼究竟是怎麼相處這種方法呢?我想其中的有一點很是重要,那就是每次都要從最右邊向左邊找到兩個相鄰的元素,使得知足小於關係。而後將小於關係左邊的數字與右邊第一個大於左邊的數字交換順序,這樣以後再將右邊的序列按照從小到大順序來排列,這樣作的好處是使得算法能繼續運行下去,最妙的是,將更大的數字交換到左邊,隨着循環順序的加深確定左邊的數字會愈來愈大,最終直至變成從大到小順序來排列,這樣就達到了目的spa

public class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        while (i >= 0 && nums[i + 1] <= nums[i]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i + 1);
    }

    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

3、總結

    遇到有些問題,是你們共同的認識而且通過長期探索獲得的,若是咱們使用傳統的方法可能須要花費很是多的時間,所以,咱們平時要多作題,從而懂得更多。code

相關文章
相關標籤/搜索