leetcode之全排列問題(Permutations)

在leetcode上,跟Permutations有關的題目:

  • 31 Next Permutation
  • 46 Permutations

一.31 Next Permutation

  31題是排列的入門題,給出[1,2,3,4],需給出下一排列[1,2,4,3]。這題有固定的解法,給定排序nums[n]=[1,4,2,7,6,5,3],n=0~6:

  1. 從序號6開始往前尋找第一對嚴格遞減(即找到第一個小於的數,從後往前看)的兩個數,在這裏是[2,7],記作[i,j],從7→2是嚴格遞減。
  2. 從序號6開始尋找第一個大於序號i的數2,找到數3序號k,交換數2序號i和數3序號k,得到[1,4,3,7,6,5,2]
  3. 將從j開始(從7開始)一直到最後的序列改爲正序(此時的序列一定是逆序的),得到[1,4,3,2,5,6,7]

這裏考慮兩種極端情況[1,2,3,4,5,6,7]和[7,6,5,4,3,2,1]。
  前一種情況:[i,j]=[6,7],[k]=[7];交換i,k,即6和7;反轉從k開始的序列,這種情況不特殊,可與一般情況的一起處理;
  後一種情況:i<0,j=0,直接全體逆序一下即可,這種情況特殊,不能和一般情況一起處理。

二.46 Permutations

本題可以有三種解法:

  1. 回溯法(此方法也是leetcode上提示的方法)
  2. 利用31題,只要知道一種排列,後續的都可以next出來
  3. 使用dfs

2.1 回溯法

  回溯法的本質是類似於枚舉的搜索嘗試過程,一般都帶着條件去搜索,如果發現繼續搜索下去也找不到最優解,那麼在此點就開始回溯。一般我們將它的解空間轉化轉化爲樹的形式,這樣便於理解。
  這裏我們以排序[1,2,3,4]爲例,畫出它的解空間樹。每當i=4的時候,說明已經找到了一個解。需要尋找下一個解。比如找到了第4層的2314後,這時已經到頭了,我們需要回溯,返回到第3層,再返回到第2層的2314,然後沿着另外一條路到了第4層的2341,這樣就找到了另一個解。
這裏寫圖片描述
  在排列問題中,沒有約束條件,沒有約束條件的回溯有點像暴力窮舉,要達到葉子結點纔會回溯。如要有條件的話,就可以對解空間樹進行剪枝,可以避免許多明顯不必要搜索的路徑。
  用遞歸實現的回溯比較簡單易懂,回溯法一般有以下模板:

//用遞歸實現回溯的一般模板
void backTrack(int i) {
    if(i > n) {
        //到達葉子結點,分析此解是否最優
        return;
    }

    for(int k=low; k<high; k++) {
        if(fx()) {//滿足約束條件
            a += nums[i];
            backTrack(i+1);
            a -= nums[i];//在回溯前進行狀態的清零
        }
    }
}

  有許多經典的問題都可以用回溯法來解決,比如8皇后問題、01揹包問題等。
  迴歸這道題目,下面給出這道題回溯解法。

public class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> arrAll = new ArrayList<List<Integer>>();
        backTrack(0, nums, arrAll);
        return arrAll;
    }

    private void backTrack(int i, int[] nums, List<List<Integer>> arrAll) {
        if(i>=nums.length) {
            List<Integer> arr = new ArrayList<Integer>();
            for (int a : nums) {
                arr.add(a);
            }
            arrAll.add(arr);
            return;
        }

        for(int k=i; k<nums.length; k++) {
            exch(nums, i, k);
            backTrack(i+1, nums, arrAll);
            exch(nums, i, k);
        }
    }

    private void exch(int[] nums, int i, int k) {
        int temp = nums[i];
        nums[i] = nums[k];
        nums[k] = temp;
    }
}

2.2 next法

  用現成的next法
 

2.3 dfs法

【Reference】
46 Permutations三種不同的解法 https://leetcode.com/discuss/20474/share-my-three-different-solutions

發佈了98 篇原創文章 · 獲贊 41 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章