[ 熱題 HOT 100]---46. 全排列➕47. 全排列 II--- 回溯法/dfs

1 題目描述

    1. 全排列
      給定一個 沒有重複 數字的序列,返回其所有可能的全排列。
    1. 全排列 II
      給定一個可包含重複數字的序列,返回所有不重複的全排列。

2 解題思路

    1. 全排列
  • 解決方法: 回溯法/dfs
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

作者:liweiwei1419
鏈接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

    1. 全排列 II

這一題是在「力扣」第 46 題: “全排列” 的基礎上增加了“序列中的元素可重複”這一條件,但要求返回的結果又不能有重複元素。

如果要比較兩個列表是否一樣,一個很顯然的辦法是分別排序,然後逐個比對既然要排序,我們可以在搜索之前就對候選數組排序,一旦發現這一支搜索下去可能搜索到重複的元素就停止搜索,這樣結果集中不會包含重複元素

在這裏插入圖片描述

在這裏插入圖片描述

這個題目相比於46題,要多做的工作就是去重

可以先排序,這個方便後面剪枝操作,所以要增加排序的代碼

然後就是判斷剪枝的條件,if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])
這裏,i>0 是爲了爲了保證 nums[i - 1] 有意義,
nums[i] == nums[i - 1],就是說該元素與前一個元素相等,這就是剪枝的關鍵,要被剪掉
寫 !used[i - 1] 是因爲 nums[i - 1] 在深度優先遍歷的過程中剛剛被撤銷選擇

還有就是循環題的第一個判斷條件,
在46題中,是if(!used[i]),但是在47題中,是if(used[i])
這也是46 和 47題的區別~~~
新加的代碼,如果已經被使用,那麼繼續下一步continue

作者:liweiwei1419
鏈接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

3 解決代碼

    1. 全排列
  • 解決方法: 回溯法/dfs《Java代碼》
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<List<Integer>>  res = new ArrayList<>();
        //特例:如果爲空,返回的是空數組
        if(len == 0){
            return res;
        }
        //used布爾類型的數組,表示有沒有使用過
        //當檢查元素是否使用的時候時間複雜度爲1, 用空間換取時間
        boolean[] used = new boolean[len];
        List<Integer> path = new ArrayList<>();
        dfs(nums, len, 0, path, used, res);
        return res;
    }

    public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
        //已經找到滿足長度的數組,可以進行返回
        if(depth == len){
            //這個地方要進行數據的複製,否則返回的是空
            res.add(new ArrayList<>(path));
            return;
        }
        //循環實現在,還未選擇的數中依次選擇一個元素作爲下一個位置的元素
        for(int i = 0; i < len; i++){
            if(!used[i]){
                //沒有被用過加入到path路徑之中,把used狀態設爲true表示已使用
                path.add(nums[i]);
                used[i] = true;
                //遞歸
                dfs(nums, len, depth + 1, path, used,res);
                //這一步就是完成狀態重製工作,從深層次節點回到淺層次節點
                //use的狀態設爲false,表示沒有使用,並且將最後加入的那個元素移除
                used[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }
}
  • 解決方法: 回溯法/dfs《python3代碼》
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def dfs(nums, size, depth, path, used, res):
            if depth == size:
                #這個地方實現的也是元素的複製
                res.append(path[:])
                return 
            for i in range(size):
                if not used[i]:
                    used[i] = True
                    path.append(nums[i])
                    dfs(nums, size, depth + 1, path, used, res)
                    used[i] = False
                    path.pop()
        size = len(nums)
        if len(nums) == 0:
            return []
        #布爾類型的數組used
        used = [False for _ in range(size)]
        res = []
        # path = []
        dfs(nums, size, 0, [], used,res)
        return res
    1. 全排列II
  • 解決方法: 回溯法/dfs《Java代碼》
class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        int len = nums.length;
        List<List<Integer>>  res = new ArrayList<>();
        //特例:如果爲空,返回的是空數組
        if(len == 0){
            return res;
        }
        //-------新加的代碼,排序剪枝,方便去重-----
        Arrays.sort(nums);
        //used布爾類型的數組,表示有沒有使用過
        //當檢查元素是否使用的時候時間複雜度爲1, 用空間換取時間
        boolean[] used = new boolean[len];
        List<Integer> path = new ArrayList<>();
        dfs(nums, len, 0, path, used, res);
        return res;
    }

    public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
        //已經找到滿足長度的數組,可以進行返回
        if(depth == len){
            //這個地方要進行數據的複製,否則返回的是空
            res.add(new ArrayList<>(path));
            return;
        }
        //循環實現在,還未選擇的數中依次選擇一個元素作爲下一個位置的元素
        for(int i = 0; i < len; i++){
            //-------新加的代碼,如果已經被使用,那麼繼續下一步-----
            if(used[i]){
                continue;
            }
            //-------新加的代碼,剪枝條件,-----
            //剪枝條件:i > 0 是爲了保證 nums[i - 1] 有意義
            // 寫 !used[i - 1] 是因爲 nums[i - 1] 在深度優先遍歷的過程中剛剛被撤銷選擇
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }
            //沒有被用過加入到path路徑之中,把used狀態設爲true表示已使用
            used[i] = true;
            path.add(nums[i]);
            //遞歸
            dfs(nums, len, depth + 1, path, used,res);
            //這一步就是完成狀態重製工作,從深層次節點回到淺層次節點
            //use的狀態設爲false,表示沒有使用,並且將最後加入的那個元素移除
            used[i] = false;
            path.remove(path.size() - 1);    
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章