回溯BackTrack

目录

有重复项数字的所有排列

组合总和2

组合总和

集合的所有子集

子集2

数字字符串转化成IP地址

括号生成

全排列

组合

路径总和2

分割回文串


​​​​​​​

有重复项数字的所有排列

给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。

import java.util.*;

public class Solution {
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    ArrayList<Integer> c = new ArrayList<>();
    public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
        for(int i = 0; i < num.length; i++){
            c.add(num[i]);
        }
        dfs(0);
        Collections.sort(res, new Comparator<ArrayList<Integer>>() {
            @Override
            public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
                for (int i = 0; i < o1.size(); i++) {
                    if(o1.get(i) == o2.get(i)) continue;
                    else if(o1.get(i) > o2.get(i)) return 1;
                    else return -1;
                }
                return 0;
            }
        });
        return res;
    }
    
    public void dfs(int x){
        if(x == c.size() - 1){
            res.add(new ArrayList<>(c));
            return;
        }
        HashSet<Integer> set = new HashSet<>();
        for(int i = x; i < c.size(); i++){
            if(set.contains(c.get(i))) continue;
            set.add(c.get(i));
            swap(i,x);
            dfs(x+1);
            swap(i,x);
        }
    }
    
    public void swap(int a, int b) {
        int tmp = c.get(a);
        c.set(a, c.get(b)); 
        c.set(b, tmp);
    }
    
}
public List<List<Integer>> permuteUnique(int[] nums) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
    return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
    if(tempList.size() == nums.length){
        list.add(new ArrayList<>(tempList));
    } else{
        for(int i = 0; i < nums.length; i++){
            if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
            used[i] = true; 
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, used);
            used[i] = false; 
            tempList.remove(tempList.size() - 1);
        }
    }
}

组合总和2

给出一组候选数 c 和一个目标数 t ,找出候选数中起来和等于 t 的所有组合。

 c 中的每个数字在一个组合中只能使用一次。

import java.util.*;

// 菜鸡解法
public class Solution {
    private ArrayList<ArrayList<Integer>> ansLists;
    private int[] num;
    private int target;
    
    public ArrayList<ArrayList<Integer>> combinationSum2(int[] num, int target) {
        // 组合要求不重复,需要与排列区分
        ArrayList<ArrayList<Integer>> ansLists = new ArrayList<>();
        if(num==null || num.length==0) return ansLists;
        
        this.ansLists = ansLists;
        this.num = num;
        this.target = target;
        // 排序方便防重复、剪枝
        Arrays.sort(num);
        dfs(new ArrayList<>(), -1, 0);
        
        return this.ansLists;
    }
    
    // 组合防止重复只能往后取
    public void dfs(ArrayList<Integer> list, int currIdx, int currSum) {
        if(currIdx==num.length-1 && currSum!=target) return;
        if(currSum==target) {
            ansLists.add(new ArrayList<Integer>(list));
            return;
        }
        
        for(int nextIdx=currIdx+1; nextIdx<num.length; ) {
            // 剪枝
            if(currSum+num[nextIdx]>target) break;
            // 回溯
            list.add(num[nextIdx]);
            dfs(list, nextIdx, currSum+num[nextIdx]);
            list.remove(list.size()-1);
            // 滑动到下一个不同值,防止重复
            nextIdx++;
            while(nextIdx<num.length && num[nextIdx-1]==num[nextIdx]) {
                nextIdx++;
            }
        }
    }
}

组合总和

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

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

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
    if(remain < 0) return;
    else if(remain == 0) list.add(new ArrayList<>(tempList));
    else{ 
        for(int i = start; i < nums.length; i++){
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements
            tempList.remove(tempList.size() - 1);
        }
    }
}

集合的所有子集

现在有一个没有重复元素的整数集合S,求S的所有子集
注意:
你给出的子集中的元素必须按升序排列

给出的解集中不能出现重复的元素

import java.util.*;

public class Solution {
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    ArrayList<Integer> ls = new ArrayList();
    public ArrayList<ArrayList<Integer>> subsets(int[] S) {
        //保证了不会重复访问
        int[] used = new int[S.length];
        
        Arrays.sort(S);
        dfs(S,used,0);
        return res;
        
    }
    private void dfs(int[] a,int[] used,int start) {
        //这一步没有return;  保证了将每个遍历到的都输出一遍
        res.add(new ArrayList(ls));
        
        for(int i = start; i < a.length; i++) {
            if(used[i] == 1) continue; 
            ls.add(a[i]);
            used[i] = 1;
            dfs(a,used,i);
            ls.remove(ls.size()-1);
            used[i] = 0;        
        }
    }
}

子集2

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例 2:

输入:nums = [0]
输出:[[],[0]]
class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        helper(res,new ArrayList<>(),nums,0);
        return res;
    }
    
    public void helper(List<List<Integer>> res, List<Integer> ls, int[] nums, int pos) {
        res.add(new ArrayList<>(ls));
        for(int i=pos;i<nums.length;i++) {
            if(i>pos&&nums[i]==nums[i-1]) continue;
            ls.add(nums[i]);
            helper(res,ls,nums,i+1);     
            ls.remove(ls.size()-1);
        }
    }
}
class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        helper(res,new ArrayList<>(),nums,0,false);
        return res;
    }
    
    public void helper(List<List<Integer>> res, List<Integer> ls, int[] nums, int pos, boolean choosePre) {
        if(pos==nums.length) {
            res.add(new ArrayList<>(ls));
            return;
        }
        helper(res,ls,nums,pos+1,false);
        if(pos>=1&&nums[pos]==nums[pos-1]&&!choosePre) return;
        ls.add(nums[pos]);
        helper(res,ls,nums,pos+1,true);
        ls.remove(ls.size()-1);
    }
}

数字字符串转化成IP地址

现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。

例如:

给出的字符串为"25525522135",

返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)

import java.util.*;
public class Solution {  //很巧妙的dfs,调了一早上
    ArrayList<String> res = new ArrayList<>();
    ArrayList<String> track = new ArrayList<>();
    public ArrayList<String> restoreIpAddresses (String s) {
        if(s.length() < 4)    return new ArrayList<>();
        dfs(s,0);
        return res;
    }
    public void dfs(String s,int start){
	    int len = s.length();
	    if(track.size() == 4 && start != len)//不合法
	        return;
	    if(track.size() == 4 && start == len){
	        res.add(track.get(0) + "." + track.get(1) + "." 
                    + track.get(2) + "."+track.get(3));
	    }
	    for(int i = start,j = i + 1;j < len + 1 && j - i <= 3;j++){
	    	if(validate(s.substring(i,j))) { //数字合法
	            track.add(s.substring(i,j));
	            dfs(s,j); //向下搜
	            track.remove(track.size() - 1);
	    	}
        }
    }
	private boolean validate(String s) {
		if(s.equals("0"))	return true;
		if(s.startsWith("0")) return false;
		return Integer.parseInt(s) <= 255;
	}
}

括号生成

给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。

例如,给出n=3,解集为:

"((()))", "(()())", "(())()", "()()()", "()(())"

import java.util.ArrayList;

public class Solution {
    /**
     * 题目:括号生成
     * 给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。
     * 例如,给出n=3,解集为:
     * "((()))", "(()())", "(())()", "()()()", "()(())",
     */
    ArrayList<String> list = new ArrayList<>();
    public ArrayList<String> generateParenthesis (int n) {
        // n:表示有n对括号,也就是n个左括号'(',n个右括号')'
        // 一个""空字符串,要么加一个左括号,要么加一个右括号
        // 当n个左右括号都加完了,那就是一个结果出来了
        // 条件一:左括号数量一定是先达到n,所以就不能再加左括号了,left>n,就错了
        // 条件二:肯定有左才有右,如果右括号先放一定错,所以要保证left >= right
        process(n, 0, 0, "");
        return list;
    }

    public void process(int n, int left, int right, String cur) {
        // 如果左括号数量>n,或者左括号数量大于右括号,直接return
        if (left > n || right > left) {
            return;
        }
        // 满足条件,放入list中
        if (left == n && right == left) {
            list.add(cur);
            return;
        }
        //要么继续加左括号,要么继续加右括号
        //加一个左括号,左括号数量+1
        process(n, left + 1, right, cur + "(");
        //加一个右括号,右括号数量+1
        process(n, left, right + 1, cur + ")");
    }
}

全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]
public List<List<Integer>> permute(int[] nums) {
   List<List<Integer>> list = new ArrayList<>();
   // Arrays.sort(nums); // not necessary
   backtrack(list, new ArrayList<>(), nums);
   return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){
   if(tempList.size() == nums.length){
      list.add(new ArrayList<>(tempList));
   } else{
      for(int i = 0; i < nums.length; i++){ 
         if(tempList.contains(nums[i])) continue; // element already exists, skip
         tempList.add(nums[i]);
         backtrack(list, tempList, nums);
         tempList.remove(tempList.size() - 1);
      }
   }
} 

组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]
    public static List<List<Integer>> combine(int n, int k) {
		List<List<Integer>> combs = new ArrayList<List<Integer>>();
		combine(combs, new ArrayList<Integer>(), 1, n, k);
		return combs;
	}
	public static void combine(List<List<Integer>> combs, List<Integer> comb, int start, int n, int k) {
		if(k==0) {
			combs.add(new ArrayList<Integer>(comb));
			return;
		}
		for(int i=start;i<=n;i++) {
			comb.add(i);
			combine(combs, comb, i+1, n, k-1);
			comb.remove(comb.size()-1);
		}
	}

路径总和2

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

public List<List<Integer>> pathSum(TreeNode root, int sum){
	List<List<Integer>> result  = new LinkedList<List<Integer>>();
	List<Integer> currentResult  = new LinkedList<Integer>();
	pathSum(root,sum,currentResult,result);
	return result;
}

public void pathSum(TreeNode root, int sum, List<Integer> currentResult,
		List<List<Integer>> result) {

	if (root == null)
		return;
	currentResult.add(new Integer(root.val));
	if (root.left == null && root.right == null && sum == root.val) {
		result.add(new LinkedList(currentResult));
		currentResult.remove(currentResult.size() - 1);//don't forget to remove the last integer
		return;
	} else {
		pathSum(root.left, sum - root.val, currentResult, result);
		pathSum(root.right, sum - root.val, currentResult, result);
	}
	currentResult.remove(currentResult.size() - 1);
}

分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]
public class Solution {
    public List<List<String>> partition(String s) {
       List<List<String>> res = new ArrayList<List<String>>();
       List<String> list = new ArrayList<String>();
       dfs(s,0,list,res);
       return res;
    }
    
    public void dfs(String s, int pos, List<String> list, List<List<String>> res){
        if(pos==s.length()) res.add(new ArrayList<String>(list));
        else{
            for(int i=pos;i<s.length();i++){
                if(isPal(s,pos,i)){
                    list.add(s.substring(pos,i+1));
                    dfs(s,i+1,list,res);
                    list.remove(list.size()-1);
                }
            }
        }
    }
    
    public boolean isPal(String s, int low, int high){
        while(low<high) if(s.charAt(low++)!=s.charAt(high--)) return false;
        return true;
    }
    
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值