1. 程式人生 > >LeetCode筆記——40組合總和Ⅱ

LeetCode筆記——40組合總和Ⅱ

題目:給定一個數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。

candidates 中的每個數字在每個組合中只能使用一次。

說明:

  • 所有數字(包括目標數)都是正整數。
  • 解集不能包含重複的組合。 

示例 1:

輸入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集為:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

輸入: candidates = [2,5,2,1,2], target = 5,
所求解集為:
[   [1,2,2],   [5] ]

思路一:

搜尋新組合時從下一個元素開始;去重時通過HashSet取出重複。在這段程式碼中定義了類變數,使得整個類中都可以使用

class Solution {        int n;    //類變數        int nums[];        List<List<Integer>>result;         

      //遞迴查詢的方法,返回結果result         public List<List<Integer>> find(List<Integer>values,int index,int reserve){             if(reserve==0){         //遞迴結束的標誌,找到了一組組合                 ArrayList<Integer>item=new ArrayList<Integer>();                 item.addAll(values);                 result.add(item);  //新增到最終的結果中             }             for(int i=index;i<=n;i++)   //遞迴尋找組合             {                 if(nums[i-1]<

=reserve){                     values.add(nums[i-1]);                     find(values,i+1,reserve-nums[i-1]); //每次相減                     values.remove(values.size()-1);  //把已經新增進去的組合刪除                 }                               }             return result;                        }          public List<List<Integer>> combinationSum2(int[] candidates, int target) {               //給類變數賦值         this.nums=candidates;         this.n=candidates.length;   //開始的時候賦值出現錯誤         this.result=new ArrayList<List<Integer>>();                Arrays.sort(nums);//陣列排序         //各種特殊情況         if(candidates.length == 0 || candidates[0] > target)             return result;                  List<List<Integer>> newList = new ArrayList<List<Integer>>();             List<List<Integer>> list = new ArrayList<List<Integer>>();         list = find(new ArrayList<Integer>(),1,target)
;                  //將find函式的結果一次加入到HashSet中。因為HashSet中不允許重複,因此如果能夠成功加入,說明不重複。將list中的結果通過set的判斷轉換到newlist中         Set<List<Integer>> set = new HashSet<List<Integer>>();//去除重複                for(int i = 0; i < list.size();i++){                         if(set.add(list.get(i))){//沒有重複                                 newList.add(list.get(i));//新增新的列表                         }               }

    return newList;     } }

思路 二

本題與39題相比主要有兩點不同。第一是本題中存在重複節點;第二是每個節點只能使用一次,類似於圖中的沒有自環出現。對於第二個問題來說,每次搜尋新路徑的時候從它的下一個節點開始就可以解決;對於去重問題,以下程式碼採用的是先對陣列進行排序,將所有相同權值的節點都放在一起,這樣可以方便跳過這些節點。

參考網上大神的程式碼:

class Solution {     public List<List<Integer>> combinationSum2(int[] candidates, int target) {        List<List<Integer>> res = new ArrayList<>();         if (candidates == null || candidates.length == 0 || target < 0) return res;         List<Integer> list = new ArrayList<>();         Arrays.sort(candidates); //排序,使得尋找相同出權值的節點變得容易         get(candidates, target, 0, list, res);         return res;     }          private void get(int[] candidates, int target, int i, List<Integer> list, List<List<Integer>> res) {         if (i > candidates.length || target < 0) return; //因為沒有自環,所以每次都是從下一個節點開始搜尋,要新增一個條件判斷節點仍在圖中         if (target == 0) { //滿足條件,新增至結果集             res.add(new ArrayList<>(list));             return;         }         for (int p = i; p < candidates.length; p++) {             list.add(candidates[p]); //新增節點到路徑             get(candidates, target - candidates[p], p+1, list, res); //因為沒有自環,所以每次搜尋更新路徑權值後的下一個節點             list.remove(list.size()-1); //回溯,將當前節點從路徑中剔除             while (p < candidates.length - 1 && candidates[p] == candidates[p+1]) p++; //因為存在重複節點,所以已經被剔除的節點不能再被放回到路徑中         }       } }

執行最快的程式碼:

具體的執行情況弄不懂。。。。。是怎麼進行去重的。。。。

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> ans = new LinkedList<>();
        Arrays.sort(candidates);
        backTrack(candidates, target, 0, ans, new LinkedList<Integer>(), false);
        return ans;
    }
    
    public void backTrack(int[] candidates, int target, int index, List<List<Integer>> ans, List<Integer> tmp, boolean used) {
        if(target == 0) {
            ans.add(new ArrayList(tmp));
        } else if(target > 0 && index < candidates.length) {
            int item = candidates[index];
            if(item <= target) {
                backTrack(candidates, target, index + 1, ans, tmp, false);
                // 去重
                if(!(!used && index > 0 && candidates[index] == candidates[index - 1])) {
                    tmp.add(item);
                    backTrack(candidates, target - item, index + 1, ans, tmp, true);
                    tmp.remove(tmp.size() - 1);
                }
            }
        }
    }
}