LeetCode刷题 之 回溯法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_32502811/article/details/82853257

LeetCode中回溯法的一些题总结

最近刷LeetCode(惭愧,前两页还没刷完···)发现在好多问题中都碰到了回溯法,并且有一个固定的模板。于是想试着总结一下,并加深记忆,希望以后碰到类似的问题能信手拈来~
利用回溯的题目,比较好识别,特点就是需要穷举才能得到答案。所以肯定是需要递归的。(吐槽一下自己,树的问题基本都需要递归,我每次碰到都要想半天···)
话不多说,上题:

  • LeetCode46 全排列
    给定一个没有重复数字的序列,返回其所有可能的全排列。
    示例:
输入:[1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

代码:

class Solution{
	public List<List<Integer>> permute(int[] nums){
		List<List<Integer>> res = new ArrayList<>();
		backtrack(res, new ArrayList<>(), nums);
		return res;
	}
	public void backtrack(List<List<Integer>> res, List<Integer> list, int[]nums){
		if(list.size() == nums.length){
			res.add(new ArrayList<>(list));
		} else {
			for(int i = 0; i < nums.length; i++){
				if(list.contains(nums[i])){
					continue;
				}
				list.add(nums[i]);
				backtrack(res, list, nums);
				list.remove(list.size()-1);
			}
		}
	}
}
  • LeetCode 47 给定一个可包含重复数字的排列,返回所有不重复的全排列
    示例:
输入:[1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

和上面的题相比,除了回溯外,为了不引入重复项,需要进行剪枝操作.因此,我们引入一个标记是否被访问过的变量。
在这里插入图片描述
图片来自微信公众号Leetcode名企之路
下面是代码

class Solution{
	public List<List<Integer>> permuteUnique(int[] nums){
		List<List<Integer>> res = new ArrayList<>();
		Arrays.sort(nums);
		boolean[] used = new boolean[nums.length];//标记是否被访问过
		backtrack(res, new ArrayList<>(), nums, used);
		return res;
	}
	public void backtrack(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] used){
		if(list.size() == nums.length){
			res.add(new ArrayList<>(list));
		} else {
			for(int i = 0; i < nums.length; i++){
				if(used[i]) continue;
				if(i > 0 && nums[i-1] == nums[i] && !used[i - 1]) continue;
				used[i] = true;
				list.add(nums[i]);
				backtrack(res, list, nums, used);
				used[i] = false;
				list.remove(list.size()-1);
			}
		}
	}
}
  • LeetCode77 组合
    给定两个整数n和k,返回1~n中所有可能的k个数的组合
    示例:
输入:n=4, k= 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4]
]

直接上代码

class Solution{
	public List<List<Integer>> combine(int n, int k){
		List<List<Integer>> res = new ArrayList<>();
		backtrack(res, new ArrayList<>(), n, k, 1);
		return res;
	}
	public void backtrack(List<List<Integer>> res, List<Integer> list, int n, int k, int start){
		if(list.size() == k){
			res.add(new ArrayList<>(list));
		} else{
			for(int i = start; i <= n; i++){
				list.add(i);
				backtrack(res, list, n, k, i+1);
				list.remove(list.size()-1);
			}
		}
	}
}
  • LeetCode78 子集
    在这里插入图片描述
    代码
class Solution{
	public List<List<Integer>> subsets(int[] nums){
		List<List<Integer>> res = new ArrayList<>();
		Arrays.sort(nums);
		backtrack(res, new ArrayList<>(), nums, 0);
		return res;
	}
	public void backtrack(List<List<Integer>> res, List<Integer> list, int[] nums, int start){
		res.add(new ArrayList<>(list));
		for(int i = start; i < nums.length; i++){
			list.add(nums[i]);
			backtrack(res,list,nums,i+1);
			list.remove(list.size()-1);
		}
	}
}

全排列和子集的区别:全排列的话需要约束每一项的元素个数,而且for循环里永远是从0开始的;子集不需要约束元素个数,因此每个解都要加入到最终解集中去,为了防止重复项,需要设置一个start,标记每次回溯从哪里开始。

代码和图参考公众号LeetCode名企之路,安利一波。

猜你喜欢

转载自blog.csdn.net/sinat_32502811/article/details/82853257
今日推荐