CombinationSum組合數之和系列問題

1. leetcode39組合數之和

給定一無重複元素數組和目標數,數組的每個元素可以重複使用找到數組中元素所有可能的組合滿足其和等於目標數,要求不能有重複的組合。

  • 經典的組合問題
  • 遞歸的出口
    • 找到一組解後,自然應當返回上一層
    • 當前的nums[i] > target,也應該直接返回上一層,因爲數組排序後的,在這一層再往後找意義不大
  • 每個數字可以重複利用,因此傳入下一層的位置不變
  • 但是這並不意味着每一層都是從nums[0]開始,每一層應該從當前位置開始nums[i],這樣就可以有效避免由於"往前看"導致的組合數重複
  • 代碼
    public static List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> results = new ArrayList<>();
        List<Integer> item = new ArrayList<>();
        if (candidates.length == 0){
            return results;
        }
        Arrays.sort(candidates);
        combinationSumHelper(candidates, target, 0, item, results);
        return results;

    }
    private static void combinationSumHelper(int[] candidates,
                                             int target,
                                             int startIndex,
                                             List<Integer> item,
                                             List<List<Integer>> results){
        if (target == 0){
            results.add( new ArrayList<>(item));
            return;
        }

        for (int i=startIndex; i<candidates.length; i++){
            //target再減就是負數了 又數組升序 繼續在這一層遍歷下一個 i 已經沒有意義了
            // 所以這裏直接返回到上一層
            if (candidates[i] > target){
                return;
            }
            item.add(candidates[i]);
            //因爲每一個元素可以重複使用 所以這裏傳入下一層的位置不變還是 i
            // 這裏一個很重要的問題在於 每一次遞歸並不是 從數組的第0位開始的
            // 而是從上一次所在位置開始的
            // 簡單的說 就是不準往前看!
            combinationSumHelper02(candidates, target-candidates[i],i,item,results);
            item.remove( item.size() - 1);
        }
    }
  • 配合下圖理解會更好
    在這裏插入圖片描述

2.leetcode40組合數之和2

給定一含重複元素的數組和一目標數,數組的元素只能使用一次求其所有可能的唯一組合,其和等於目標數

  • 含重複元素
  • 數組的元素只能使用一次
  • 所以相比於上題,本地的最大難點就在於去重
  • 這裏去重的思路和求子集2的思路完全一致
  • 首先排序
  • 其次對於重複元素,只能從左往右依次添加,不能跳躍
    public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<Integer> item = new ArrayList<>();
        List<List<Integer>> results = new ArrayList<>();
        if (candidates.length == 0){
            results.add(item);
            return results;
        }
        Arrays.sort(candidates);
        helper02(candidates, target, 0, item, results);

        return results;
    }
   private static void helper02(int[] candidates,
                               int target,
                               int startIndex,
                               List<Integer> item,
                               List<List<Integer>> results){

        if ( target == 0){
            results.add( new ArrayList<>(item));
            return;
        }
        for (int i=startIndex; i<candidates.length; i++){
            // 規定重複元素只能從左到右依次取 去重思路和子集問題完全一樣
            if ( i!=0 && candidates[i] == candidates[i-1] && i!=startIndex){
                continue;
            }

            if (candidates[i] > target){
                return;
            }
            item.add( candidates[i] );
            //題目要求元素 只能用一次 所以i+1
            helper02(candidates, target - candidates[i], i+1, item, results);
            item.remove( item.size() - 1);
        }
    }

3.leetcode216組合數之和3

從數字1-9之間挑選k個數,其和等於n,求所有可能的組合

  • 和上述兩題幾乎一樣
  • 沒有重複元素,不需要考慮去重
  • 要求必須爲k個數,所以添加到結果集的時候需要多一個判斷條件

 public static List<List<Integer>> combinationSum3(int k, int n) {
        int[] nums = {1,2,3,4,5,6,7,8,9};
        List<Integer> item = new ArrayList<>();
        List<List<Integer>> results = new ArrayList<>();
        helper(nums, k, n, 0, item, results);
        return results;

    }
  private static void helper(int[] nums,
                                 int k,
                                 int n,
                                 int startIdnex,
                                 List<Integer> item,
                                 List<List<Integer>> results){
        if ( n == 0 && item.size() == k){
            results.add( new ArrayList<>(item));
            return;
        }

        for (int i = startIdnex; i<nums.length; i++){

            if (item.size() > k){
                continue;
            }
            if (nums[i] > n){
                return;
            }
            item.add(nums[i]);
            helper02( nums, k, n - nums[i],i+1,item, results);
            item.remove( item.size() - 1);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章