回溯是一种比较特殊的递归,这种递归有以下两个特点:
1、程序处于尝试搜索的状态,选择条件向某个分支执行,当不满足情况时,回溯到先前的分叉点
2、到达某一终止条件时,记录数据并返回
回溯题的常见模板伪代码:
def backTrack(路径, 可选列表) :if 路径长度 == 可选列表长度或满足其他条件:记录该路径for 可选项 in 可选列表 :添加可选项(另外根据情况判断是否需要将该可选项进行剔除)backTrack(路径, 可选列表)删除可选项(另外根据情况判断是否需要将该可选项进行回添)
全排列问题
- 全排列

全排列的主要目的是取出一个数后就不再放入,那么可以通过visited数组来表示哪个数据是否访问过,进而再取那些并么有被访问过的数据
class Solution {List<List<Integer>> list;public List<List<Integer>> permute(int[] nums) {list = new ArrayList<>();boolean[] visited = new boolean[nums.length];trackBack(new ArrayList<>(), nums, visited);return list;}public void trackBack(List<Integer> list1, int[] nums, boolean[] visited) {if (list1.size() == nums.length) {list.add(new ArrayList<>(list1));return;}for (int i = 0; i < nums.length; i++) {if (visited[i]) {continue;}visited[i] = true;list1.add(nums[i]);trackBack(list1, nums, visited);list1.remove(list1.size() - 1);visited[i] = false;}}}
- 全排列II

对于全排列2而言,主要增加难度在于如何处理数组中的重复数据,主要的方式是通过数组排序并判定相邻数据中是否相同。如果相同并且该数据先前是否已经访问过,则跳过;否则对其进行遍历;**
class Solution {List<List<Integer>> list;public List<List<Integer>> permuteUnique(int[] nums) {list = new ArrayList<>();boolean[] visited = new boolean[nums.length];List<Integer> list1 = new ArrayList<>();if(nums.length == 0)return list;Arrays.sort(nums);dfs(list1, nums, visited);return list;}public void dfs(List<Integer> list1, int[] nums, boolean[] visited){//设置终止条件:if(list1.size() == nums.length) {list.add(new ArrayList<>(list1));return;}for(int i = 0; i < nums.length; i++) {if (!visited[i]) {//相比于第一类全排列,最关键的判定条件://首先需要将输入数组进行排序,之后判断,如果位置i上的数等于位置i-1上的数,//并且visited[i-1]=false说明这个数在上一个排列中已经在这个位置放置过了,//那么说明该位置不需要在放置重复的数if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1])continue;visited[i] = true;list1.add(nums[i]);dfs(list1, nums, visited);visited[i] = false;list1.remove(list1.size() - 1);}}}}
组合总和问题
- 数组总和

直接套用回溯公式即可解决:
class Solution {List<List<Integer>> list;public List<List<Integer>> combinationSum(int[] candidates, int target) {list = new ArrayList<>();trackBack(new ArrayList<>(), candidates, target, 0);return list;}public void trackBack(List<Integer> list1, int[] candidates, int target, int index) {if (target == 0) {list.add(new ArrayList<>(list1));return;}for (int i = index; i < candidates.length; i++) {if (target < 0) {break;}list1.add(candidates[i]);trackBack(list1, candidates, target - candidates[i], i);list1.remove(list1.size() - 1);}}}
- 组合总和II

该题关键在于如何处理去重复的问题,一般通过对整个序列进行排序并且在对序列遍历时略过相等部分
class Solution {List<List<Integer>> list;public List<List<Integer>> combinationSum2(int[] candidates, int target) {list = new ArrayList<>();Arrays.sort(candidates);trackBack(new ArrayList<>(), candidates, target, 0);return list;}public void trackBack(List<Integer> list1, int[] candidates, int target, int index) {if (target == 0) {list.add(new ArrayList<>(list1));return;}for (int i = index; i < candidates.length; i++) {if (target < 0) {break;}if (i > index && candidates[i] == candidates[i - 1]) {continue;}list1.add(candidates[i]);trackBack(list1, candidates, target - candidates[i], i + 1);list1.remove(list1.size() - 1);}}}
