leetcode回溯法总结

本文深入解析回溯法的基本思想与实现步骤,通过LeetCode经典题目如39、40、46、47、78、90、216等,展示如何运用回溯法解决组合与排列问题,特别关注重复元素处理。

第二遍做leetcode 中的回溯法, 发现不光没有更好的理解,甚至遗忘了很多。 所以今天觉得做一篇有关回溯法的总结

首先,回溯法的基本思想:从一条路往前走,能进则进,不能进则退回,换一条路再试

回溯法的基本定义和概念
首先要明确下面三个概念
1约束函数:约束函数是根据题意定出, 通过描述合法解的一般特征用于去除不合法的解,从而避免继续搜索出这个不合法解的剩余部分

2状态空间树
状态空间树是一个对所有姐的图形描述,树上每个子节点都只有一个部分与父结点不同

3扩展结点,活结点,死节点
所谓扩展结点,就是当前正在求出的子节点的节点,在dfs中只允许洋浦一个扩展结点,活结点就i是通过与约束函数的对照,揭谛艾那本事何其父节点本身均满足约束函数要求的节点

leetcode 中回需要回溯法的题目有 39, 40,46,47,78,90,216

过DFS思想完成回溯,完整过程如下:
(1) 设置初始化的方案(给变量赋初值,读入已知数据等)。
(2) 变换方式去试探,若全部试完则转(7)。
(3) 判断此法是否成功(通过约束函数),不成功则转(2)。
(4) 试探成功则前进一步再试探。
(5) 正确方案还未找到则转(2)。
(6) 已找到一种方案则记录并打印。
(7) 退回一步(回溯),若未退到头则转(2)。
(8) 已退到头则结束或打印无解。

回溯方法的步骤如下:

定义一个解空间,它包含问题的解。
用适于搜索的方式组织该空间。
用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。

首先来看39题
添加链接描述
Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

The same repeated number may be chosen from candidates unlimited number of times.

题目中说道给定一个数组candidates(无重复)和target, 找到所有combinatiion 和等于target

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        if(candidates == null || candidates.length == 0){
            return res;
        }
        
        helper(res, new ArrayList<>(), candidates,target,0);
        return res;
    }
    public void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, int target,int start){
            if(target <0){
                return;
            }
        
            if(target == 0){
                res.add(new ArrayList<>(list));
                return;
            }
        
            for(int i = start; i < candidates.length;i++){
                list.add(candidates[i]);
                  helper(res, list,candidates,target- candidates[i],i);
                list.remove(list.size()-1);
            }
    }
}

40题
和上题相差不多 但是candidate可能会重复
所以题目需要去重,其他与上一题基本无差

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        if(candidates == null || candidates.length<1)return res;
        
        helper(res,new ArrayList<Integer>(), candidates, target, 0);
        return res;
    }
    
    public void helper( List<List<Integer>> res,List<Integer> list, int[] candidates, int target,int start){
       
        if(target == 0){
            res.add(new ArrayList<>(list));
           
        }
        
        for(int i =start; i < candidates.length && target >= candidates[i];i++){
            if(i>start && candidates[i] == candidates[i-1])continue;
            list.add(candidates[i]);
            helper(res,list, candidates, target- candidates[i], i+1);
            list.remove(list.size()-1);
        }
    }
}```



46,
Given a collection of distinct integers, return all possible permutations.

Example:

Input: [1,2,3]
Output:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

给定一个由不同的 interger 组成的collection,返回所有可能的排列组合 
```java
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new LinkedList();
        List<Integer> numsList = new ArrayList();
        for(int e : nums) numsList.add(e);
        permutation(result, numsList, 0);
        return result;
    }
    private void permutation(List<List<Integer>> result, List<Integer> nums, int li){
        result.add(new ArrayList(nums)); // 效率主要提高在这,native 拷贝方法会比写 for 循环拷贝要快
        // 从li开始顺序两两交换
        for(int i = li; i < nums.size(); ++i) {
            for(int j = i+1; j < nums.size(); ++j) {
                Collections.swap(nums, i, j);
                permutation(result, nums, i+1);
                Collections.swap(nums, i, j);
            }
        }
    }
}

47,

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

Example:

Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
给定一个由可能重复的的 interger 组成的collection,返回所有可能的排列组合

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
         List<List<Integer>> res = new ArrayList<>();
        if(nums == null){
            return res;
        }
        helper(res, nums,0);
        return res;
    
    }
    private void helper( List<List<Integer>> res, int [] nums, int index){
        
        if(index == nums.length){
            List<Integer> temp = new ArrayList<>();
            for(int num:nums){
                temp.add(num);
            
            }
            res.add(temp);
            return;
        }
        
        Set<Integer> appeared = new HashSet<>();
        for(int i = index; i < nums.length; ++i){
            if(appeared.add(nums[i])){
                swap(nums,index, i);
                helper(res,nums,index+1);
                swap(nums,index, i);
            }
        }
    }
    
    private void swap(int[] nums, int i, int j){
        int save = nums[i];
        nums[i] = nums[j];
        nums[j] = save;
    }
    }

78,
given a set of distinct integers, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

给定一个不同整数构成的集合,返回所有的子集

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> temp = new ArrayList<>();
        
        
        
        helper(res,temp,nums,0);
        return res;
    }
    
    public void helper(List<List<Integer>> res, List<Integer> temp, int [] nums, int start){
        res.add(new ArrayList<Integer>(temp));
        for(int i = start; i < nums.length; i++){
            temp.add(nums[i]);
            helper(res,temp,nums,i+1);
            temp.remove(temp.size() - 1);
        }
        
    }
}

90,Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: [1,2,2]
Output:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
给定一个右可能重复的整数构成的集合,返回所有的子集

 class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
         List<List<Integer>>  res = new ArrayList<>();
        
        if(nums == null){
            return res;
        }
        
        Arrays.sort(nums);
        List<Integer> list = new ArrayList<>();
        dfs(res,list,nums,0);
        return res;
    }
    
    private void dfs( List<List<Integer>>  res , List<Integer> list, int []nums, int start){
        res.add(new ArrayList<>(list));
        
        for(int i = start; i < nums.length; i++){
            if(i != 0 && nums[i] == nums[i -1] && i > start ){
                continue;
            }
            
            list.add(nums[i]);
            dfs(res,list,nums,i+1);
            list.remove(list.size() -1);
        }
        
    }
}

216

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值