全排列、子集合subset、目標和combation

主要的方法

  1. 深度優先搜索,回溯算法

  2. 寬度優先搜索

  3. 是否有相同元素需要考慮等問題

針對所給問題,確定問題的解空間:

  • 首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。

  • 確定結點的擴展搜索範圍 for等一系列循環等問題

  • 以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。 判斷減支情況

int a[n];
  try(int i)
{
   if(i>n)
     輸出結果;
   else
  {
    for(j = 下界; j <= 上界; j=j+1)  
                    // 枚舉i所有可能的路徑
         {
    if(fun(j))      // 滿足限界函數和約束條件
             {
          a[i] = j;
                         // 其他操作
            try(i+1);
                 回溯前的清理工作(如a[i]置空值等);
                 }
            }
     }
  }

題目一

子集合,subset 不包括相同元素的情況

  1. 深度遞歸優先搜索算法
// Recursion
class Solution {
public:
    vector<vector<int> > subsets(vector<int> &S) {
        vector<vector<int> > res;
        vector<int> out;
        sort(S.begin(), S.end());
        getSubsets(S, 0, out, res);
        return res;
    }
    void getSubsets(vector<int> &S, int pos, vector<int> &out, vector<vector<int> > &res) {
        res.push_back(out);
        // pos 是下界, size 是上界, 這也是
        for (int i = pos; i < S.size(); ++i) {
            out.push_back(S[i]);
            getSubsets(S, i + 1, out, res);
            out.pop_back();
        }
    }
};

整個過程添加如下的情況

不包含相同元素
如果包含相同的元素的話,判斷前後兩個元素是否相等,如果相等直接跳過去就可以了

public List<List<Integer>> subsetsWithDup(int[] nums) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, 0);
    return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int start){
    list.add(new ArrayList<>(tempList));
// subset 是從start 開始,而全排列是從全部的元素重新開始即可
    for(int i = start; i < nums.length; i++){
    // 如果[1,2,2,] 的話,我們直接跳過去就可以了
        if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates
        tempList.add(nums[i]);
        backtrack(list, tempList, nums, i + 1);
        tempList.remove(tempList.size() - 1);
    }
} 

全排列的問題

全排列和subset 的區別主要是元素的個數哪裏

for 代表解空間樹節點的可以擴展的方向是哪裏的

  • start 代表下界,size代表上界的基本情況

subset

// 開始遍歷的順序不一樣的
for(int i = start;i<nums.size();i++)

全排列

//所有的節點都得重新開始考慮的,上界是數組
for (int i=0;i<nums.size();i++)

combination sum

  1. 每個數字可以重複使用的情況,一個數字可以使用多次的情況,並且數組裏面沒有重複
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result =new ArrayList<>();
        if(candidates==null ||candidates.length==0)
            return result;
        Arrays.sort(candidates);
        backdfs(candidates,target,0,new ArrayList<>(),result);
        return result;
    }
    private void backdfs(int [] candidates,int remain, int start, List<Integer>tmp, List<List<Integer>>result)
    {
        //如果不滿足直接停止往下搜索
        if(remain<0)
            return;
        //滿足條件接着所示,
        if(remain==0)
            result.add(new ArrayList<>(tmp));
        for(int i=start;i<candidates.length;i++){
            //處理當前節點元素
            tmp.add(candidates[i]);
            //往下搜索
            backdfs(candidates,remain-candidates[i],i,tmp,result);
            //在該條路徑上面搜索結束,退回上一層節點
            tmp.remove(tmp.size()-1);
        }
    }
}
  1. 數組裏面有重複元素,並且每個元素只能會用一遍的情況
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  List<List<Integer>> list = new LinkedList<List<Integer>>();
  Arrays.sort(candidates);
  backtrack(list, new ArrayList<Integer>(), candidates, target, 0);
  return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] cand, int remain, int start) {
  
  if(remain < 0) return; /** no solution */
  else if(remain == 0) list.add(new ArrayList<>(tempList));
  else{
     for (int i = start; i < cand.length; i++) {
        if(i > start && cand[i] == cand[i-1]) continue; /** skip duplicates */
        tempList.add(cand[i]);
        //元素不可重用的情況
        backtrack(list, tempList, cand, remain - cand[i], i+1);
        tempList.remove(tempList.size() - 1);
     }
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章