博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
回溯法常用的解题模板和常见题型
阅读量:4185 次
发布时间:2019-05-26

本文共 9551 字,大约阅读时间需要 31 分钟。

文章目录

1、什么是回溯法

wiki上面是这么说的

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

其实通俗点讲我觉得回溯法就是使用在对一组数据求其在某种条件下所有的可能性组合。

2、通用框架

常见的题目通常是给一个数组数据或者字符串,然后求其所有组成。所以有如下的通用解题模板

Class Solation{
public List
> backtrack(int[] a) {
List
> list = new ArrayList<>(); //Arrays.sort(a);//当a中存在重复值。而重复值不能使用的时候。就要进行排序。对使用过的重复值不再使用 backTrackTemp(list, new ArrayList<>(), a, .....)//其中的"...."表示其他限定条件(根据条件限定而存在与否) return lis}//回溯过程private static void backTrackTemp(List
> list, Arraylist
tempList, int[] a,....){ //终止条件,也就是一次结果或者不符合条件 if(false)//false代表条件不符合 return false; if(true)//当符合需要的结果 list.add(new ArrayList(tempList))//注意这里要重新创建,因为tempList是一个对象,改变的话,会改变结果值。所以重新创建 //对每个值进行回溯 for(int i = start; i < a.length; i++) { if(true)//存在某个限定条件的,比如出现重复值,跳过(根据条件限定而存在与否) continue; mask(used(i));//将i标记为已使用(根据条件限定而存在与否) backTrackTemp(list, tempList, a, i+1)//此处的i+1也可以根据实际情况判断题目中的数字是否可以重复使用 unmask(used(i));//回溯完要记得取消掉 tempList.remove(tempList.size() - 1);//回溯回父节点.寻找下一个节点 }}}

这里对backTrackTemp(list, new ArrayList<>(), a, .....)中的参数说个说明

1、list:是用来保存最终结果集
2、new ArrayList<>()是用来保存中间产生的结果集
3、a,给定条件
4、…其他一些限定条件
前面三个条件是必须存在的。后面的4根据题目要求而进行的更改

接下来我们通过一些例子来看看是怎么使用这个demo

3、模板测试

1、subSet

1、数组中不存在重复数值的

题目如下:

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
> subsets(int[] nums) {
List
> list = new ArrayList<>(); backTracking(list,new ArrayList<>(),nums,0);//list,临时list,额外条件,因为subSet是不能存在重复的,所以要有start值。因为在set中只要元素相同,即使顺序不用也是一致的。跟排列不一样 return list; } //没有start就找不到终止条件了 private static void backTracking(List
> list, ArrayList
tempList, int[] nums,int start){ list.add(new ArrayList<>(tempList));//每一组都添加,不用条件,这一步已经把空的也加进去 for(int i = start; i < nums.length; i++) { tempList.add(nums[i]); backTracking(list,tempList,nums,i+1); tempList.remove(tempList.size() - 1); } }}

2、数组中存在重复数值的,但是结果不能存在重复值

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
> subsetsWithDup(int[] nums) {
List
> list = new ArrayList<>(); Arrays.sort(nums); backTracking(list, new ArrayList<>(), nums, 0); return list; } //因为这个回溯法是判断以每一个数为准的所有组成 private static void backTracking(List
> list, List
tempList, int[] nums, int startIndex) { //方法一:判断是否已经存在 // if(!list.contains(tempList)) list.add(new ArrayList<>(tempList)); for(int i = startIndex; i < nums.length; i++) { if(i > startIndex && nums[i] == nums[i-1]) continue;//方法二.跳过重复的数字 tempList.add(nums[i]); backTracking(list, tempList, nums, i + 1); tempList.remove(tempList.size()-1); } }}
2、Permutations

1、不存在重复值的排列

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]]
class Solution {
public static List
> permute(int[] nums) {
List
> list = new ArrayList<>(); backtrack(list, new ArrayList<>(), nums,new boolean[nums.length]);//回溯法的三要素,条件值nums,存储结果数组list,临时结果(判断用的)。还有其他的额外控制条件 return list; } private static void backtrack(List
> list, List
tempList, int [] nums, boolean[] used){ if(tempList.size() == nums.length){ //终止条件 list.add(new ArrayList<>(tempList));//这边要new的原因是因为下面的对象引用还存在。更改会改变这个list里面的值 } else{ for(int i = 0; i < nums.length; i++){ //回溯过程 if(used[i] == true) continue; else { tempList.add(nums[i]); used[i] = true;//访问过了就不要再访问 backtrack(list, tempList, nums, used); used[i] = false;//一次回溯后就要将访问的重新置为未访问的 tempList.remove(tempList.size() - 1); } } } } }

2、存在重复数字

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]]
class Solution {
public List
> permuteUnique(int[] nums) {
List
> list = new ArrayList<>(); Arrays.sort(nums); backTracking(list, new ArrayList<>(), nums, new boolean[nums.length]); return list; } private void backTracking(List
> list, ArrayList
tempList, int[] nums, boolean[] used){ //终止条件 //if(tempList.size() == nums.length && !list.contains(tempList))//用contains判断是否已经存在 if(tempList.size() == nums.length) list.add(new ArrayList(tempList)); //回溯条件 for(int i = 0; i < nums.length; i++) { if((i > 0 && nums[i] == nums[i - 1] && used[i-1]==false)||(used[i] == true))//访问过了就不再访问且判断重复的去掉 continue; tempList.add(nums[i]);//添加到tempList中 used[i] = true;//置为访问过的 backTracking(list, tempList, nums, used);//以当前点回溯 used[i] = false; tempList.remove(tempList.size() - 1);//返回上一层 } }}
3、 Combination Sum

1、不存在重复值的

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.Note:All numbers (including target) will be positive integers.The solution set must not contain duplicate combinations.Example 1:Input: candidates = [2,3,6,7], target = 7,A solution set is:[  [7],  [2,2,3]]Example 2:Input: candidates = [2,3,5], target = 8,A solution set is:[  [2,2,2,2],  [2,3,3],  [3,5]]
class Solution {
public List
> combinationSum(int[] candidates, int target) {
List
> list = new ArrayList<>(); backTracking(list, new ArrayList<>(), candidates, target, 0); return list; } private static void backTracking(List
> list, ArrayList
tempList, int[] candidates, int target, int startIndex) { if(target < 0)return;//当不符合的时候,返回 if(target == 0)list.add(new ArrayList<>(tempList));//符合的时候添加到集合 for(int i = startIndex; i < candidates.length; i++) { tempList.add(candidates[i]); //回溯,因为一个节点可以重复使用,所以还是以i为当前节点 backTracking(list, tempList, candidates, target - candidates[i], i); tempList.remove(tempList.size() - 1); } }}

2、存在重复值的

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.Each number in candidates may only be used once in the combination.Note:All numbers (including target) will be positive integers.The solution set must not contain duplicate combinations.Example 1:Input: candidates = [10,1,2,7,6,1,5], target = 8,A solution set is:[  [1, 7],  [1, 2, 5],  [2, 6],  [1, 1, 6]]Example 2:Input: candidates = [2,5,2,1,2], target = 5,A solution set is:[  [1,2,2],  [5]]
class Solution {
public List
> combinationSum2(int[] candidates, int target) {
List
> list = new ArrayList<>(); Arrays.sort(candidates); backTracking(list, new ArrayList<>(), candidates, target, 0); return list; } private static void backTracking(List
> list, ArrayList
tempList, int[] candidates, int target, int startIndex ) { if(target < 0)return; if(target == 0)list.add(new ArrayList<>(tempList));//终止条件 for(int i = startIndex; i < candidates.length; i++) { if(i > startIndex && candidates[i] == candidates[i-1])//去掉重复的 continue; tempList.add(candidates[i]); backTracking(list, tempList, candidates, target - candidates[i],i+1);//每个数字只能用一次 tempList.remove(tempList.size() - 1); } }}
4、Palindrome Partitioning
Given a string s, partition s such that every substring of the partition is a palindrome.Return all possible palindrome partitioning of s.Example:Input: "aab"Output:[  ["aa","b"],  ["a","a","b"]]
class Solution {
public List
> partition(String s) {
List
> list = new ArrayList<>(); backTracking(list, new ArrayList<>(), s, 0); return list; } private static void backTracking(List
> list, List
tempList, String s, int startIndex){ //终止条件,也就是加入list的条件,startIndex来到最后一个字符。遍历完了 if(startIndex == s.length()) list.add(new ArrayList<>(tempList)); for(int i = startIndex; i < s.length(); i++) { if(isPartition(s, startIndex, i))//如果是回文串, { tempList.add(s.substring(startIndex,i+1));//加入到templist.substring(start, end),end是取不到的 backTracking(list, tempList, s, i+1);//当前部分是回文串就不会再使用 tempList.remove(tempList.size() - 1); } } } //判断是否是回文串 private static boolean isPartition(String s, int left, int right) { while(left <= right) { if(s.charAt(left++) != s.charAt(right--)) return false; } return true; }}

4、总结

我们通过subset(所有可能子集合),Permutations(所有可能排序), Combination Sum(可以组合成一个数的所有情况)、Palindrome Partitioning(一个字符串拆成都是回文串的所有个数)。

1、从中可以发现。如果跟顺序没有关系的,则应该加startIndex,而如果有关系则都应该从0开始。因为不同位置从0开始都是不同的。
2、如果在一次中访问过的可能再访问到且不能再访问,则应该设置一个used[]数组来保存每次访问过的元素。当一次回溯完时则应该放开。
3、如果存在重复的数字。则应该先排序,排序可以更好的解决去掉重复值的情况。

转载地址:http://sxrai.baihongyu.com/

你可能感兴趣的文章
Object-c 语法 - NSObject常用方法和反射
查看>>
QTP参数化
查看>>
需求分析&用例编写
查看>>
SAP 播放语言 转载自http://www.cnblogs.com/sapSB/p/6043129.html
查看>>
关于hashcode 和 equals 的内容总结
查看>>
22-强迫症序列
查看>>
springboot中的常用注解
查看>>
Java中的集合框架
查看>>
linux ftp服务器搭建
查看>>
python xml转excle
查看>>
步进电机与伺服电机
查看>>
自适应模糊控制——间接自适应模糊控制
查看>>
优美的函数
查看>>
Maven 3 Felix 4 Eclipse 的搭建与部署(部分转载自别人文章)
查看>>
leetcode--shell
查看>>
Shell简介
查看>>
python 函数赋值
查看>>
2、vector的实现
查看>>
程序员工资那么高,却从不炫富?网友回复让人“笑喷了”!
查看>>
2017.5.2 java中StringTokenizer的用法及示例
查看>>