(超级无敌重要,背下来背下来)Leetcode(46,47,78,90,39,40)dfs和回溯(全排列)

删除线格式 简单的打印全排列:dfs,用标记数组used标记是否用过

package leetcode160;


public class queue {
	static int count=0;
	public static void main(String[] args) {
		char[] chs= {'a','b','c'};
		int[] used=new int[3];
		char[] temp=new char[3];
		dfs(0,chs,temp,used);
	}
	public static void dfs(int deep,char[] chs,char[] temp,int[] used) 
	{
		if(deep==chs.length) {
			count++;
			System.out.println(count+"--"+String.valueOf(temp));
			return ;
		}
		for(int i=0;i<chs.length;i++) {
			if(used[i]==0) {
				temp[deep]=chs[i];
				used[i]=1;
				dfs(deep+1,chs,temp,used);
				used[i]=0;
			}
		}
	}
	
}

46题的全排列,放在list里面的

class Solution {
    List<List<Integer>> res=new ArrayList();
    public List<List<Integer>> permute(int[] nums) {
        List<Integer> temp=new ArrayList();
        int[] used=new int[nums.length];
        dfs(0,temp,nums,used);
        return res;
    }
    public void dfs(int deep,List<Integer> temp,int[] nums,int[] used){
        if(deep==nums.length){
            res.add(temp);
            return ;
        }
        for(int i=0;i<nums.length;i++){
            if(used[i]==0){
                temp.add(nums[i]);
                used[i]=1;//标记已经用过,后续的dfs就不会使用了
                dfs(deep+1,new ArrayList(temp),nums,used);//每次都要放一个新的temp进去,不能用一个,没一次逗用一个新的,当前temp当前有效
                temp.remove(temp.size()-1);//这个不要忘了,因为temp已经变了,必须改回来
                used[i]=0;//桶上
            }
        }
    }
}

我感觉回溯和dfs是一样的,都是一起用的,或者说我感觉dfs更是一种方法,而回溯是个思想,例如dfs可以一直深入下去,而加上回溯可以遍历更多中可能,当然dfs也可以遍历更多的可能,那就是回溯咯

看到一句话:A general approach to backtracking questions in Java (Subsets, Permutations, Combination Sum, Palindrome Partioning),翻译就是回溯主要用于子集,排列,组合,回文

leetcode78
方法1:递归探索

class Solution {
    List<List<Integer>> res=new ArrayList();
    public List<List<Integer>> subsets(int[] nums) {
        List<Integer> temp=new ArrayList();
        digui(0,nums,temp);
        return res;
    }
    public void digui(int deep,int[] nums,List temp){
        if(deep==nums.length){
            res.add(temp);
            return ;
        }
        //有
        temp.add(nums[deep]);
        digui(deep+1,nums,new ArrayList(temp));
        //没有
        temp.remove(temp.size()-1);
        digui(deep+1,nums,new ArrayList(temp));
        
    }
}

方法二:回溯

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

47 全排列2

这个代码那个used[i-1]==0的条件应该改成==1,因为等于0,反正我是解释不通,。。。。但是确实可以,速度慢了一倍,==1可行,是因为排序好了,重复元素一定在一起,如果i和i-1相等,前面的没用过,那么在这个循环之前i-1一定用了(仅限当前递归的for循环,因为for是顺序循环),同一个递归表示一个位置,不能重复,所以continue。
class Solution {
    List<List<Integer>> res=new ArrayList();
    public List<List<Integer>> permuteUnique(int[] nums) {
        int[] used=new int[nums.length];
        Arrays.sort(nums);
        dfs(0,nums,new ArrayList<Integer>(),used);
        return res;
    }
    public void dfs(int deep,int[] nums,List temp,int[] used){
        if(deep==nums.length){
            res.add(temp);
            return ;
        }
        for(int i=0;i<nums.length;i++){
            if(used[i]==1) continue;
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==1) continue;
            temp.add(nums[i]);
            used[i]=1;
            dfs(deep+1,nums,new ArrayList(temp),used);
            used[i]=0;
            temp.remove(temp.size()-1);
        }
    }
}

方法2:交换法,这个其实就是递归尝试的完美体现,看这个链接全排列其实就是先确定第一个位置,然后对后面的递归求全排列,依次类推。

class Solution {
    List<List<Integer>> res=new ArrayList();
    public List<List<Integer>> permuteUnique(int[] nums) {
        dfs(0,nums);
        return res;
    }
    public void dfs(int deep,int[] nums){
        if(deep==nums.length-1){
            List list=new ArrayList();
            for(int i:nums){
                list.add(i);
            }
            res.add(list);
        }
        Set<Integer>  set=new HashSet();
        for(int i=deep;i<nums.length;i++){
            //if(deep!=i&&nums[deep]==nums[i]) continue;
            //这个出错了,大问题,大问题,因为我只考虑了第一个和其他后面的元素换的时候,出现1就不换,但是我没考虑到这样的
            //1,2,2,2,  这样的,会和2换3次,但是换3次是没有用的,只要换一次就行,只有元素不同,全排列才会不同的
            //所有用set,根据插入成功确定是否是置换过的相同元素
            if(!set.add(nums[i])) continue;
            swap(nums,deep,i);
            dfs(deep+1,nums);
            swap(nums,deep,i);
        }
    }
    public void swap(int[] nums,int i,int j){
        int temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
}

leetcode39组合求和
非常常规的递归回溯,模板问题,和子集有相似之处,就是,7为根对吧,有2,3,4,6选择,然后跟新target之后,2有2,3,6,而3的选择是没有2的,因为2,3和3,2是一样的

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

leetcode40 组合求和2
有重复元素,所有元素也只能使用一次,而且所求的组合不能重复,这就要求很搞了,记住一定要先排序,只要有重复元素 就先排序,而且如何判断,如何跳过重复呢,就是看他前一个是否用过了(用过是递归层,而重复判断是平行层),因为排序好了,所以前一个如果没用过,那么平行层一定用了,这时候就挑,如果前一个被用了,说明递归层用了,这时候就可以用。

class Solution {
    List<List<Integer>> res=new ArrayList();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        List temp=new ArrayList();
        int[] used=new int[candidates.length];
        dfs(candidates,target,used,temp,0);
        return res;
    }
    public void dfs(int[] candidates,int target,int[] used,List temp,int deep){
        if(target==0){
            res.add(temp);
            return ;
        }
        if(target<0) return ;
        for(int i=deep;i<used.length;i++){
            if(i>deep&&candidates[i]==candidates[i-1]&&used[i-1]==0) continue;
            temp.add(candidates[i]);
            used[i]=1;
            dfs(candidates,target-candidates[i],used,new ArrayList(temp),i+1);
            temp.remove(temp.size()-1);
            used[i]=0;
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值