赞
踩
DFS 是一个劲的往某一个方向搜索,而回溯算法建立在 DFS 基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与 DFS 的区别就是有无状态重置
当问题需要 "回头",以此来查找出所有的解的时候,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止
①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关), 与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择
(子集、组合)、全排列、搜索
A、 子集 - 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)
- class Solution {
- public List<List<Integer>> subsets(int[] nums) {
- List<List<Integer>> res = new ArrayList<>();
- backtrack(nums, res, 0, new ArrayList<Integer>());
- return res;
- }
-
- private void backtrack(int[] nums, List<List<Integer>> res, int start, ArrayList<Integer> tmp) {
- res.add(new ArrayList<>(tmp));
- for(int i = start; i < nums.length; i++) {
- tmp.add(nums[i]);
- backtrack(nums, res, i + 1, tmp);
- tmp.remove(tmp.size() - 1);
- }
- }
- }
B、子集 II(剪枝思想)
- class Solution {
- public List<List<Integer>> subsetsWithDup(int[] nums) {
- Arrays.sort(nums);
- List<List<Integer>> res = new ArrayList<>();
- backtrack(nums, res, 0, new ArrayList<>());
- return res;
- }
- private void backtrack(int[] nums, List<List<Integer>> res, int start, ArrayList<Integer> tmp) {
- res.add(new ArrayList<>(tmp));
- for(int i = start; i < nums.length; i++) {
- if(i > start && nums[i] == nums[i-1]) { //同一层级不出现相同元素;允许不同层级出现相同元素
- continue;
- }else {
- tmp.add(nums[i]);
- backtrack(nums, res, i + 1, tmp);
- tmp.remove(tmp.size() - 1);
- }
- }
- }
- }
C、组合总和
方法一、回溯法
- class Solution {
- public List<List<Integer>> combinationSum(int[] candidates, int target) {
- Arrays.sort(candidates);
- List<List<Integer>> res = new ArrayList<>();
- backtrack(candidates, res, 0, 0, target, new ArrayList<>());
- return res;
- }
- private void backtrack(int[] nums, List<List<Integer>> res, int start, int sum, int target, ArrayList<Integer> tmp) {
- if(sum == target) { //满足条件
- res.add(new ArrayList<>(tmp));
- return ;
- }
- for(int i = start; i < nums.length; i++) {
- if(sum > target) { //剪枝
- continue;
- }
- tmp.add(nums[i]);
- backtrack(nums, res, i, sum + nums[i], target, tmp);
- tmp.remove(tmp.size() - 1);
- }
- }
- }
方法二:
- import java.util.*;
-
- public class Solution {
-
- public List<List<Integer>> combinationSum(int[] candidates, int target) {
- int len = candidates.length;
- List<List<Integer>> res = new ArrayList<>();
- if (len == 0) {
- return res;
- }
- // 排序是剪枝的前提
- Arrays.sort(candidates);
- //初始化栈
- Deque<Integer> path = new ArrayDeque<>();
- int begin = 0; //起始位点
-
- dfs(candidates, begin, len, target, path, res);
- return res;
- }
-
- private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
- // 由于进入更深层的时候,小于 0 的部分被剪枝,因此递归终止条件值只判断等于 0 的情况
- if (target == 0) {
- res.add(new ArrayList<>(path));
- return;
- }
-
- for (int i = begin; i < len; i++) {
- // 重点理解这里剪枝,前提是候选数组有序,
- if (target - candidates[i] < 0) {
- break;
- }
- path.addLast(candidates[i]);
- dfs(candidates, i, len, target - candidates[i], path, res);
- path.removeLast();
- }
- }
- }
①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择
- backtrack的公式:
-
- result = []
- def backtrack(路径, 选择列表):
- if 满足结束条件:
- result.add(路径)
- return
-
- for 选择 in 选择列表:
- 做选择
- backtrack(路径, 选择列表)
- 撤销选择
什么时候使用 used 数组,什么时候使用 begin 变量
排列问题,讲究顺序,需要记录哪些数字已经使用过,此时用 used 数组;
组合问题,不讲究顺序,需要按照某种顺序搜索,此时使用 begin 变量。
40. 组合总和 II
- class Solution {
- public List<List<Integer>> combinationSum2(int[] candidates, int target) {
- int len = candidates.length;
- List<List<Integer>> res = new ArrayList<>();
- if(len == 0){
- return res;
- }
- Arrays.sort(candidates); // 先排序以使用剪枝
- Deque<Integer> path = new ArrayDeque<>();
- int begin = 0;
- dfs(candidates, begin, len, target, path, res);
- return res;
- }
-
- private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res){
- //大剪枝,
- if(target == 0){
- res.add(new ArrayList<>(path));
- return;
- }
-
- for(int i = begin; i < len; i++){
- // 小剪枝:同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发生重复,因此跳过,用 continue
- if (i > begin && candidates[i] == candidates[i - 1]) {
- continue;
- }
- if(target - candidates[i] < 0){
- break;
- }
- path.add(candidates[i]);
- dfs(candidates, i + 1, len, target - candidates[i], path, res);
- path.removeLast();
- }
-
- }
- }
46. 全排列
47. 全排列 II
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。