全排列、子集合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);
    }
} 

全排列和子集合问题的不同

子集合不同判断元素个数问题,直接添加即可, 并且要指定pos, 然后在dfs 里面再pos +1,的情况

全排列的问题

全排列和subset 的区别主要是元素的个数哪里

for 代表解空间树节点的可以扩展的方向是哪里的

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

subset

// 开始遍历的顺序不一样的

 public void dfs  {
      for(int i = start;i<nums.size();i++)
      {


     dfs( i+1, )
     }
}

全排列

类似于宽度优先搜索,因此数组里面的元素每个元素都需要使用得到的,因此我们

//所有的节点都得重新开始考虑的,上界是数组
public void dfs{
      for (int i=0;i<nums.size();i++)
     {
     // 判断tmp list 里面是否已经存在该元素
        //判断该元素是否已经使用过的情况下来处理即可的问题
			dfs(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);
     }
  }
}

树的路径和

从根节点到叶子节点情况,只要满足该种情况即可,然后在这里面我们需要注意的是向下搜索的方向,从i+1,变成了向左右孩子节点走下棋的情况

并且扩张方向只有一个节点,所有也是没有for 循环该种情况发生过你,而是中间结果直接add

public List<List<Integer>> pathsum(TreeNode root, int sum){
          List<List<Integer>> result =new List<>();
          if(root==null)
                  return result;
          List<Integer> current= new ArrayList<>();
          dfs(result,root,current,sum);
          return reuslt;
          }
    pulic private void  dfs(TreeNode root,int sum, List<List<Integer>> result, List<Integer>current){
    // 递归的出口信息
    if(root==null)
    	return ;
    // 满足条件的情况
    current.add(root.val);
    if(root.left==null && root.right==null&& root.val==sum)
      {
       	  result.add(current);
       	  current.remove(current.size()-1);
       	  return;
      }
       dfs(root.left,sum-root.val,result,current);
       dfs(root.right,sum-root.val,result,current);
       current.remove(current.size()-1);
    }
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值